vmhgfs-only/0000755000000000000000000000000012025726746012040 5ustar rootrootvmhgfs-only/autoconf/0000755000000000000000000000000012025726745013655 5ustar rootrootvmhgfs-only/autoconf/geninclude.c0000444000000000000000000000226412025726745016140 0ustar rootroot/********************************************************* * Copyright (C) 2003 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 * *********************************************************/ #include #ifdef CONFIG_X86_VOYAGER APATH/mach-voyager #endif #ifdef CONFIG_X86_VISWS APATH/mach-visws #endif #ifdef CONFIG_X86_NUMAQ APATH/mach-numaq #endif #ifdef CONFIG_X86_BIGSMP APATH/mach-bigsmp #endif #ifdef CONFIG_X86_SUMMIT APATH/mach-summit #endif #ifdef CONFIG_X86_GENERICARCH APATH/mach-generic #endif APATH/mach-default vmhgfs-only/autoconf/setnice.c0000444000000000000000000000216312025726745015453 0ustar rootroot/********************************************************* * Copyright (C) 2005 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 * *********************************************************/ /* * set_user_nice appeared in 2.4.21. But some distros * backported it to older kernels. */ #include #include #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 21) #include void test(void) { set_user_nice(current, -20); } #endif vmhgfs-only/autoconf/cachecreate.c0000444000000000000000000000320612025726745016247 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ #include #include /* * All kernels before 2.6.22 take 6 arguments. All kernels since * 2.6.23-rc1 take 5 arguments. Only kernels between 2.6.22 and * 2.6.23-rc1 are questionable - we could ignore them if we wanted, * nobody cares about them even now. But unfortunately RedHat is * re-releasing 2.6.X-rc kernels under 2.6.(X-1) name, so they * are releasing 2.6.23-rc1 as 2.6.22-5055-something, so we have * to do autodetection for them. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) /* Success... */ #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) #error "This test intentionally fails on 2.6.23 and newer kernels." #else #include struct kmem_cache *kmemtest(void) { return kmem_cache_create("test", 12, 0, 0, NULL, NULL); } #endif vmhgfs-only/autoconf/cachector.c0000444000000000000000000000326612025726745015761 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ #include #include /* * Between 2.6.23 and 2.6.24-rc1 ctor prototype was changed from * ctor(ptr, cache, flags) to ctor(cache, ptr). Unfortunately there * is no typedef for ctor, so we have to redefine kmem_cache_create * to find out ctor prototype. This assumes that kmem_cache_create * takes 5 arguments and not 6 - that change occured between * 2.6.22 and 2.6.23-rc1. If prototype matches, then this is old * kernel. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) #error "This test intentionally fails on 2.6.24 and newer kernels." #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) #include struct kmem_cache *kmem_cache_create(const char *, size_t, size_t, unsigned long, void (*)(void *, struct kmem_cache *, unsigned long)); #endif vmhgfs-only/autoconf/cachector1.c0000444000000000000000000000307312025726745016036 0ustar rootroot/********************************************************* * 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 * *********************************************************/ #include #include /* * Between 2.6.27-rc1 and 2.6.27-rc2 ctor prototype was changed from * ctor(cache, ptr) to ctor(ptr). Unfortunately there * is no typedef for ctor, so we have to redefine kmem_cache_create * to find out ctor prototype. If prototype matches, then this is old * kernel. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) #error "This test intentionally fails on 2.6.28 and newer kernels." #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) #include struct kmem_cache *kmem_cache_create(const char *, size_t, size_t, unsigned long, void (*)(struct kmem_cache *, void *)); #endif vmhgfs-only/autoconf/epoll.c0000444000000000000000000000235512025726745015137 0ustar rootroot/********************************************************* * Copyright (C) 2004 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 * *********************************************************/ /* * Detect whether we have 'struct poll_wqueues' * 2.6.x kernels always had this struct. Stock 2.4.x kernels * never had it, but some distros backported epoll patch. */ #include #include #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) #include void poll_test(void) { struct poll_wqueues test; return poll_initwait(&test); } #endif vmhgfs-only/autoconf/getsb1.c0000444000000000000000000000307412025726745015210 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ #include #include #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) #include /* * Around 2.6.18, a pointer to a vfsmount was added to get_sb. Red Hat * backported this behavior into a 2.6.17 kernel. * * This test will fail on a kernel with such a patch. */ static struct super_block * LinuxDriverGetSb(struct file_system_type *fs_type, int flags, const char *dev_name, void *rawData) { return 0; } struct file_system_type fs_type = { .get_sb = LinuxDriverGetSb }; #else #error "This test intentionally fails on 2.6.19 or newer kernels." #endif vmhgfs-only/autoconf/statfs1.c0000444000000000000000000000266712025726745015417 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ #include #include #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) #include /* * Around 2.6.18, the super_block pointer in statfs was changed to a dentry * pointer. Red Hat backported this behavior into a 2.6.17 kernel. * * This test will fail on a kernel with such a patch. */ static int LinuxDriverStatFs(struct super_block *sb, struct kstatfs *stat) { return 0; } struct super_operations super_ops = { .statfs = LinuxDriverStatFs }; #else #error "This test intentionally fails on 2.6.19 and newer kernels." #endif vmhgfs-only/autoconf/inode1.c0000444000000000000000000000270112025726745015176 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ #include #include #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) #include #include /* NULL */ /* * After 2.6.18, inodes were "slimmed". This involved removing the union * that encapsulates inode private data (and using i_private instead), as well * as removing i_blksize. Red Hat backported this behavior into a 2.6.17 * kernel. * * This test will fail on a kernel with such a patch. */ void test(void) { struct inode inode; inode.u.generic_ip = NULL; } #else #error "This test intentionally fails on 2.6.20 and newer kernels." #endif vmhgfs-only/autoconf/maxbytes.c0000444000000000000000000000250112025726745015651 0ustar rootroot/********************************************************* * Copyright (C) 2007 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 * *********************************************************/ #include #include /* * In 2.4.3, the s_maxbytes field was added to struct super_block. * However, we can't simply condition on 2.4.3 as the field's starting point * because the 2.4.2-2 kernel in RH7.1 also contained it, and if we don't set * it, the generic write path in the page cache will fail. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 3) #include void test(void) { struct super_block sb; sb.s_maxbytes = 0; } #endif vmhgfs-only/vmware.h0000444000000000000000000000354112025726745013512 0ustar rootroot/********************************************************* * Copyright (C) 2003 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 * *********************************************************/ /* * vmware.h -- * * Standard include file for VMware source code. */ #ifndef _VMWARE_H_ #define _VMWARE_H_ #define INCLUDE_ALLOW_USERLEVEL #define INCLUDE_ALLOW_VMCORE #define INCLUDE_ALLOW_MODULE #define INCLUDE_ALLOW_VMMON #define INCLUDE_ALLOW_VMNIXMOD #define INCLUDE_ALLOW_VMKERNEL #define INCLUDE_ALLOW_VMK_MODULE #define INCLUDE_ALLOW_DISTRIBUTE #include "includeCheck.h" #include "vm_basic_types.h" #include "vm_basic_defs.h" #include "vm_assert.h" /* * Global error codes. Currently used internally, but may be exported * to customers one day, like VM_E_XXX in vmcontrol_constants.h */ typedef enum VMwareStatus { VMWARE_STATUS_SUCCESS, /* success */ VMWARE_STATUS_ERROR, /* generic error */ VMWARE_STATUS_NOMEM, /* generic memory allocation error */ VMWARE_STATUS_INSUFFICIENT_RESOURCES, /* internal or system resource limit exceeded */ VMWARE_STATUS_INVALID_ARGS /* invalid arguments */ } VMwareStatus; #define VMWARE_SUCCESS(s) ((s) == VMWARE_STATUS_SUCCESS) #endif // ifndef _VMWARE_H_ vmhgfs-only/vm_basic_types.h0000444000000000000000000005624212025726745015226 0ustar rootroot/********************************************************* * Copyright (C) 1998-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 * *********************************************************/ /* * * vm_basic_types.h -- * * basic data types. */ #ifndef _VM_BASIC_TYPES_H_ #define _VM_BASIC_TYPES_H_ #define INCLUDE_ALLOW_USERLEVEL #define INCLUDE_ALLOW_VMMEXT #define INCLUDE_ALLOW_MODULE #define INCLUDE_ALLOW_VMMON #define INCLUDE_ALLOW_VMNIXMOD #define INCLUDE_ALLOW_VMKERNEL #define INCLUDE_ALLOW_VMKDRIVERS #define INCLUDE_ALLOW_VMK_MODULE #define INCLUDE_ALLOW_DISTRIBUTE #define INCLUDE_ALLOW_VMCORE #define INCLUDE_ALLOW_VMIROM #include "includeCheck.h" /* STRICT ANSI means the Xserver build and X defines Bool differently. */ #if !defined(__STRICT_ANSI__) || defined(__FreeBSD__) typedef char Bool; #endif #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif #define IsBool(x) (((x) & ~1) == 0) #define IsBool2(x, y) ((((x) | (y)) & ~1) == 0) /* * Macros __i386__ and __ia64 are intrinsically defined by GCC */ #ifdef __i386__ #define VM_I386 #endif #ifdef _WIN64 #define __x86_64__ #endif #ifdef __x86_64__ #define VM_X86_64 #define VM_I386 #define vm_x86_64 (1) #else #define vm_x86_64 (0) #endif #ifdef _WIN32 /* safe assumption for a while */ #define VM_I386 #endif #ifdef _MSC_VER typedef unsigned __int64 uint64; typedef signed __int64 int64; #pragma warning (3 :4505) // unreferenced local function #pragma warning (disable :4018) // signed/unsigned mismatch #pragma warning (disable :4761) // integral size mismatch in argument; conversion supplied #pragma warning (disable :4305) // truncation from 'const int' to 'short' #pragma warning (disable :4244) // conversion from 'unsigned short' to 'unsigned char' #pragma warning (disable :4267) // truncation of 'size_t' #if !defined VMX86_DEVEL // XXX until we clean up all the code -- edward #pragma warning (disable :4133) // incompatible types - from 'struct VM *' to 'int *' #pragma warning (disable :4047) // differs in levels of indirection #endif #pragma warning (disable :4146) // unary minus operator applied to unsigned type, result still unsigned #pragma warning (disable :4142) // benign redefinition of type #elif __GNUC__ /* The Xserver source compiles with -ansi -pendantic */ #ifndef __STRICT_ANSI__ #if defined(VM_X86_64) typedef unsigned long uint64; typedef long int64; #else typedef unsigned long long uint64; typedef long long int64; #endif #elif __FreeBSD__ typedef unsigned long long uint64; typedef long long int64; #endif #else #error - Need compiler define for int64/uint64 #endif typedef unsigned int uint32; typedef unsigned short uint16; typedef unsigned char uint8; typedef int int32; typedef short int16; typedef char int8; /* * FreeBSD (for the tools build) unconditionally defines these in * sys/inttypes.h so don't redefine them if this file has already * been included. [greg] * * This applies to Solaris as well. */ /* * Before trying to do the includes based on OS defines, see if we can use * feature-based defines to get as much functionality as possible */ #ifdef HAVE_INTTYPES_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_INTTYPES_H #include #endif #ifdef HAVE_STDINT_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #if !defined(USING_AUTOCONF) # if defined(__FreeBSD__) || defined(sun) # ifdef KLD_MODULE # include # else # if (BSD_VERSION >= 50) # include # include # else # include # endif # endif # elif defined __APPLE__ # if KERNEL # include # include /* mostly for size_t */ # include # else # include # include # include # include # endif # else # if !defined(__intptr_t_defined) && !defined(intptr_t) # define __intptr_t_defined # define intptr_t intptr_t # ifdef VM_I386 # ifdef VM_X86_64 typedef int64 intptr_t; # else typedef int32 intptr_t; # endif # endif # endif # ifndef _STDINT_H # ifdef VM_I386 # ifdef VM_X86_64 typedef uint64 uintptr_t; # else typedef uint32 uintptr_t; # endif # endif # endif # endif #endif /* * Time * XXX These should be cleaned up. -- edward */ typedef int64 VmTimeType; /* Time in microseconds */ typedef int64 VmTimeRealClock; /* Real clock kept in microseconds */ typedef int64 VmTimeVirtualClock; /* Virtual Clock kept in CPU cycles */ /* * Printf format specifiers for size_t and 64-bit number. * Use them like this: * printf("%"FMT64"d\n", big); * * FMTH is for handles/fds. */ #ifdef _MSC_VER #define FMT64 "I64" #ifdef VM_X86_64 #define FMTSZ "I64" #define FMTPD "I64" #define FMTH "I64" #else #define FMTSZ "I" #define FMTPD "I" #define FMTH "I" #endif #elif __GNUC__ #define FMTH "" #if defined(N_PLAT_NLM) || defined(sun) || \ (defined(__FreeBSD__) && (__FreeBSD__ + 0) && ((__FreeBSD__ + 0) < 5)) /* * Why (__FreeBSD__ + 0)? See bug 141008. * Yes, we really need to test both (__FreeBSD__ + 0) and * ((__FreeBSD__ + 0) < 5). No, we can't remove "+ 0" from * ((__FreeBSD__ + 0) < 5). */ #ifdef VM_X86_64 #define FMTSZ "l" #define FMTPD "l" #else #define FMTSZ "" #define FMTPD "" #endif #elif defined(__linux__) \ || (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) \ || (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L) \ || (defined(_POSIX2_VERSION) && _POSIX2_VERSION >= 200112L) /* BSD/Darwin, Linux */ #define FMTSZ "z" #define FMTPD "t" #else /* Systems with a pre-C99 libc */ #define FMTSZ "Z" #ifdef VM_X86_64 #define FMTPD "l" #else #define FMTPD "" #endif #endif #ifdef VM_X86_64 #define FMT64 "l" #elif defined(sun) || defined(__APPLE__) || defined(__FreeBSD__) #define FMT64 "ll" #else #define FMT64 "L" #endif #else #error - Need compiler define for FMT64 and FMTSZ #endif /* * Suffix for 64-bit constants. Use it like this: * CONST64(0x7fffffffffffffff) for signed or * CONST64U(0x7fffffffffffffff) for unsigned. * * 2004.08.30(thutt): * The vmcore/asm64/gen* programs are compiled as 32-bit * applications, but must handle 64 bit constants. If the * 64-bit-constant defining macros are already defined, the * definition will not be overwritten. */ #if !defined(CONST64) || !defined(CONST64U) #ifdef _MSC_VER #define CONST64(c) c##I64 #define CONST64U(c) c##uI64 #elif __GNUC__ #ifdef VM_X86_64 #define CONST64(c) c##L #define CONST64U(c) c##uL #else #define CONST64(c) c##LL #define CONST64U(c) c##uLL #endif #else #error - Need compiler define for CONST64 #endif #endif /* * Use CONST3264/CONST3264U if you want a constant to be * treated as a 32-bit number on 32-bit compiles and * a 64-bit number on 64-bit compiles. Useful in the case * of shifts, like (CONST3264U(1) << x), where x could be * more than 31 on a 64-bit compile. */ #ifdef VM_X86_64 #define CONST3264(a) CONST64(a) #define CONST3264U(a) CONST64U(a) #else #define CONST3264(a) (a) #define CONST3264U(a) (a) #endif #define MIN_INT32 ((int32)0x80000000) #define MAX_INT32 ((int32)0x7fffffff) #define MIN_UINT32 ((uint32)0) #define MAX_UINT32 ((uint32)0xffffffff) #define MIN_INT64 (CONST64(0x8000000000000000)) #define MAX_INT64 (CONST64(0x7fffffffffffffff)) #define MIN_UINT64 (CONST64U(0)) #define MAX_UINT64 (CONST64U(0xffffffffffffffff)) typedef uint8 *TCA; /* Pointer into TC (usually). */ /* * Type big enough to hold an integer between 0..100 */ typedef uint8 Percent; #define AsPercent(v) ((Percent)(v)) #define CHOOSE_PERCENT AsPercent(101) typedef uintptr_t VA; typedef uintptr_t VPN; typedef uint64 PA; typedef uint32 PPN; typedef uint64 PhysMemOff; typedef uint64 PhysMemSize; /* The Xserver source compiles with -ansi -pendantic */ #ifndef __STRICT_ANSI__ typedef uint64 BA; #endif typedef uint32 BPN; typedef uint32 PageNum; typedef unsigned MemHandle; typedef int32 World_ID; #define INVALID_WORLD_ID ((World_ID)0) typedef World_ID User_CartelID; #define INVALID_CARTEL_ID INVALID_WORLD_ID typedef User_CartelID User_SessionID; #define INVALID_SESSION_ID INVALID_CARTEL_ID typedef User_CartelID User_CartelGroupID; #define INVALID_CARTELGROUP_ID INVALID_CARTEL_ID typedef uint32 Worldlet_ID; #define INVALID_WORLDLET_ID ((Worldlet_ID)0) /* world page number */ typedef uint32 WPN; /* The Xserver source compiles with -ansi -pendantic */ #ifndef __STRICT_ANSI__ typedef uint64 MA; typedef uint32 MPN; #endif /* * This type should be used for variables that contain sector * position/quantity. */ typedef uint64 SectorType; /* * Linear address */ typedef uintptr_t LA; typedef uintptr_t LPN; #define LA_2_LPN(_la) ((_la) >> PAGE_SHIFT) #define LPN_2_LA(_lpn) ((_lpn) << PAGE_SHIFT) #define LAST_LPN ((((LA) 1) << (8 * sizeof(LA) - PAGE_SHIFT)) - 1) #define LAST_LPN32 ((((LA32)1) << (8 * sizeof(LA32) - PAGE_SHIFT)) - 1) #define LAST_LPN64 ((((LA64)1) << (8 * sizeof(LA64) - PAGE_SHIFT)) - 1) /* Valid bits in a LPN. */ #define LPN_MASK LAST_LPN #define LPN_MASK32 LAST_LPN32 #define LPN_MASK64 LAST_LPN64 /* * On 64 bit platform, address and page number types default * to 64 bit. When we need to represent a 32 bit address, we use * types defined below. * * On 32 bit platform, the following types are the same as the * default types. */ typedef uint32 VA32; typedef uint32 VPN32; typedef uint32 LA32; typedef uint32 LPN32; typedef uint32 PA32; typedef uint32 PPN32; typedef uint32 MA32; typedef uint32 MPN32; /* * On 64 bit platform, the following types are the same as the * default types. */ typedef uint64 VA64; typedef uint64 VPN64; typedef uint64 LA64; typedef uint64 LPN64; typedef uint64 PA64; typedef uint64 PPN64; typedef uint64 MA64; typedef uint64 MPN64; /* * VA typedefs for user world apps. */ typedef VA32 UserVA32; typedef VA64 UserVA64; typedef UserVA32 UserVAConst; /* Userspace ptr to data that we may only read. */ typedef UserVA64 UserVA64Const; /* Used by 64-bit syscalls until conversion is finished. */ #ifdef VMKERNEL typedef UserVA32 UserVA; #else typedef void * UserVA; #endif /* * Maximal possible PPN value (errors too) that PhysMem can handle. * Must be at least as large as MAX_PPN which is the maximum PPN * for any region other than buserror. */ #define PHYSMEM_MAX_PPN ((PPN)0xffffffff) #define MAX_PPN ((PPN)0x1fffffff) /* Maximal observable PPN value. */ #define INVALID_PPN ((PPN)0xffffffff) #define INVALID_BPN ((BPN) 0x1fffffff) #define INVALID_MPN ((MPN)-1) #define MEMREF_MPN ((MPN)-2) #define RESERVED_MPN ((MPN) 0) /* Support 43 bits of address space. */ #define MAX_MPN ((MPN)0x7fffffff) #define INVALID_LPN ((LPN)-1) #define INVALID_VPN ((VPN)-1) #define INVALID_LPN64 ((LPN64)-1) #define INVALID_PAGENUM ((PageNum)-1) #define INVALID_WPN ((WPN) -1) /* * Format modifier for printing VA, LA, and VPN. * Use them like this: Log("%#"FMTLA"x\n", laddr) */ #if defined(VMM64) || defined(FROBOS64) || vm_x86_64 || defined __APPLE__ # define FMTLA "l" # define FMTVA "l" # define FMTVPN "l" #else # define FMTLA "" # define FMTVA "" # define FMTVPN "" #endif #define EXTERN extern #define CONST const #ifndef INLINE # ifdef _MSC_VER # define INLINE __inline # else # define INLINE inline # endif #endif /* * Annotation for data that may be exported into a DLL and used by other * apps that load that DLL and import the data. */ #if defined(_WIN32) && defined(VMX86_IMPORT_DLLDATA) # define VMX86_EXTERN_DATA extern __declspec(dllimport) #else // !_WIN32 # define VMX86_EXTERN_DATA extern #endif #if defined(_WIN32) && !defined(VMX86_NO_THREADS) #define THREADSPECIFIC __declspec(thread) #else #define THREADSPECIFIC #endif /* * Due to the wonderful "registry redirection" feature introduced in * 64-bit Windows, if you access any key under HKLM\Software in 64-bit * code, you need to open/create/delete that key with * VMKEY_WOW64_32KEY if you want a consistent view with 32-bit code. */ #ifdef _WIN32 #ifdef _WIN64 #define VMW_KEY_WOW64_32KEY KEY_WOW64_32KEY #else #define VMW_KEY_WOW64_32KEY 0x0 #endif #endif /* * Consider the following reasons functions are inlined: * * 1) inlined for performance reasons * 2) inlined because it's a single-use function * * Functions which meet only condition 2 should be marked with this * inline macro; It is not critical to be inlined (but there is a * code-space & runtime savings by doing so), so when other callers * are added the inline-ness should be removed. */ #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3) /* * Starting at version 3.3, gcc does not always inline functions marked * 'inline' (it depends on their size). To force gcc to do so, one must use the * extra __always_inline__ attribute. */ # define INLINE_SINGLE_CALLER INLINE __attribute__((__always_inline__)) # if defined(VMM) \ && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 1)) # warning Verify INLINE_SINGLE_CALLER '__always_inline__' attribute (did \ monitor size change?) # endif #else # define INLINE_SINGLE_CALLER INLINE #endif /* * Used when a hard guaranteed of no inlining is needed. Very few * instances need this since the absence of INLINE is a good hint * that gcc will not do inlining. */ #if defined(__GNUC__) && defined(VMM) #define ABSOLUTELY_NOINLINE __attribute__((__noinline__)) #endif /* * Attributes placed on function declarations to tell the compiler * that the function never returns. */ #ifdef _MSC_VER #define NORETURN __declspec(noreturn) #elif __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 9) #define NORETURN __attribute__((__noreturn__)) #else #define NORETURN #endif /* * GCC 3.2 inline asm needs the + constraint for input/ouput memory operands. * Older GCCs don't know about it --hpreg */ #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2) # define VM_ASM_PLUS 1 #else # define VM_ASM_PLUS 0 #endif /* * Branch prediction hints: * LIKELY(exp) - Expression exp is likely TRUE. * UNLIKELY(exp) - Expression exp is likely FALSE. * Usage example: * if (LIKELY(excCode == EXC_NONE)) { * or * if (UNLIKELY(REAL_MODE(vc))) { * * We know how to predict branches on gcc3 and later (hopefully), * all others we don't so we do nothing. */ #if (__GNUC__ >= 3) /* * gcc3 uses __builtin_expect() to inform the compiler of an expected value. * We use this to inform the static branch predictor. The '!!' in LIKELY * will convert any !=0 to a 1. */ #define LIKELY(_exp) __builtin_expect(!!(_exp), 1) #define UNLIKELY(_exp) __builtin_expect((_exp), 0) #else #define LIKELY(_exp) (_exp) #define UNLIKELY(_exp) (_exp) #endif /* * GCC's argument checking for printf-like functions * This is conditional until we have replaced all `"%x", void *' * with `"0x%08x", (uint32) void *'. Note that %p prints different things * on different platforms. Argument checking is enabled for the * vmkernel, which has already been cleansed. * * fmtPos is the position of the format string argument, beginning at 1 * varPos is the position of the variable argument, beginning at 1 */ #if defined(__GNUC__) # define PRINTF_DECL(fmtPos, varPos) __attribute__((__format__(__printf__, fmtPos, varPos))) #else # define PRINTF_DECL(fmtPos, varPos) #endif #if defined(__GNUC__) # define SCANF_DECL(fmtPos, varPos) __attribute__((__format__(__scanf__, fmtPos, varPos))) #else # define SCANF_DECL(fmtPos, varPos) #endif /* * UNUSED_PARAM should surround the parameter name and type declaration, * e.g. "int MyFunction(int var1, UNUSED_PARAM(int var2))" * */ #ifndef UNUSED_PARAM # if defined(__GNUC__) # define UNUSED_PARAM(_parm) _parm __attribute__((__unused__)) # else # define UNUSED_PARAM(_parm) _parm # endif #endif /* * REGPARM defaults to REGPARM3, i.e., a requent that gcc * puts the first three arguments in registers. (It is fine * if the function has fewer than three args.) Gcc only. * Syntactically, put REGPARM where you'd put INLINE or NORETURN. */ #if defined(__GNUC__) # define REGPARM0 __attribute__((regparm(0))) # define REGPARM1 __attribute__((regparm(1))) # define REGPARM2 __attribute__((regparm(2))) # define REGPARM3 __attribute__((regparm(3))) # define REGPARM REGPARM3 #else # define REGPARM0 # define REGPARM1 # define REGPARM2 # define REGPARM3 # define REGPARM #endif /* * ALIGNED specifies minimum alignment in "n" bytes. */ #ifdef __GNUC__ #define ALIGNED(n) __attribute__((__aligned__(n))) #else #define ALIGNED(n) #endif /* *********************************************************************** * STRUCT_OFFSET_CHECK -- */ /** * * \brief Check if the actual offsef of a member in a structure * is what is expected * * * \param[in] STRUCT Structure the member is a part of. * \param[in] MEMBER Member to check the offset of. * \param[in] OFFSET Expected offset of MEMBER in STRUCTURE. * \param[in] DEBUG_EXTRA Additional bytes to be added to OFFSET to * compensate for extra info in debug builds. * *********************************************************************** */ #ifdef VMX86_DEBUG #define STRUCT_OFFSET_CHECK(STRUCT, MEMBER, OFFSET, DEBUG_EXTRA) \ ASSERT_ON_COMPILE(vmk_offsetof(STRUCT, MEMBER) == (OFFSET + DEBUG_EXTRA)) #else #define STRUCT_OFFSET_CHECK(STRUCT, MEMBER, OFFSET, DEBUG_EXTRA) \ ASSERT_ON_COMPILE(vmk_offsetof(STRUCT, MEMBER) == OFFSET) #endif /* *********************************************************************** * STRUCT_SIZE_CHECK -- */ /** * * \brief Check if the actual size of a structure is what is expected * * * \param[in] STRUCT Structure whose size is to be checked. * \param[in] SIZE Expected size of STRUCT. * \param[in] DEBUG_EXTRA Additional bytes to be added to SIZE to * compensate for extra info in debug builds. * *********************************************************************** */ #ifdef VMX86_DEBUG #define STRUCT_SIZE_CHECK(STRUCT, SIZE, DEBUG_EXTRA) \ ASSERT_ON_COMPILE(sizeof(STRUCT) == (SIZE + DEBUG_EXTRA)) #else #define STRUCT_SIZE_CHECK(STRUCT, SIZE, DEBUG_EXTRA) \ ASSERT_ON_COMPILE(sizeof(STRUCT) == SIZE) #endif /* * __func__ is a stringified function name that is part of the C99 standard. The block * below defines __func__ on older systems where the compiler does not support that * macro. */ #if defined(__GNUC__) \ && ((__GNUC__ == 2 && __GNUC_MINOR < 96) \ || (__GNUC__ < 2)) # define __func__ __FUNCTION__ #endif /* * Once upon a time, this was used to silence compiler warnings that * get generated when the compiler thinks that a function returns * when it is marked noreturn. Don't do it. Use NOT_REACHED(). */ #define INFINITE_LOOP() do { } while (1) /* * On FreeBSD (for the tools build), size_t is typedef'd if _BSD_SIZE_T_ * is defined. Use the same logic here so we don't define it twice. [greg] */ #ifdef __FreeBSD__ # ifdef _BSD_SIZE_T_ # undef _BSD_SIZE_T_ # ifdef VM_I386 # ifdef VM_X86_64 typedef uint64 size_t; # else typedef uint32 size_t; # endif # endif /* VM_I386 */ # endif # ifdef _BSD_SSIZE_T_ # undef _BSD_SSIZE_T_ # define _SSIZE_T # define __ssize_t_defined # define _SSIZE_T_DECLARED # ifdef VM_I386 # ifdef VM_X86_64 typedef int64 ssize_t; # else typedef int32 ssize_t; # endif # endif /* VM_I386 */ # endif #else # ifndef _SIZE_T # define _SIZE_T # ifdef VM_I386 # ifdef VM_X86_64 typedef uint64 size_t; # else typedef uint32 size_t; # endif # endif /* VM_I386 */ # endif # if !defined(FROBOS) && !defined(_SSIZE_T) && !defined(ssize_t) && \ !defined(__ssize_t_defined) && !defined(_SSIZE_T_DECLARED) # define _SSIZE_T # define __ssize_t_defined # define _SSIZE_T_DECLARED # ifdef VM_I386 # ifdef VM_X86_64 typedef int64 ssize_t; # else typedef int32 ssize_t; # endif # endif /* VM_I386 */ # endif #endif /* * Format modifier for printing pid_t. On sun the pid_t is a ulong, but on * Linux it's an int. * Use this like this: printf("The pid is %"FMTPID".\n", pid); */ #ifdef sun # ifdef VM_X86_64 # define FMTPID "d" # else # define FMTPID "lu" # endif #else # define FMTPID "d" #endif /* * Format modifier for printing uid_t. On sun the uid_t is a ulong, but on * Linux it's an int. * Use this like this: printf("The uid is %"FMTUID".\n", uid); */ #ifdef sun # ifdef VM_X86_64 # define FMTUID "u" # else # define FMTUID "lu" # endif #else # define FMTUID "u" #endif /* * Format modifier for printing mode_t. On sun the mode_t is a ulong, but on * Linux it's an int. * Use this like this: printf("The mode is %"FMTMODE".\n", mode); */ #ifdef sun # ifdef VM_X86_64 # define FMTMODE "o" # else # define FMTMODE "lo" # endif #else # define FMTMODE "o" #endif /* * Format modifier for printing time_t. Most platforms define a time_t to be * a long int, but on FreeBSD (as of 5.0, it seems), the time_t is a signed * size quantity. Refer to the definition of FMTSZ to see why we need silly * preprocessor arithmetic. * Use this like this: printf("The mode is %"FMTTIME".\n", time); */ #if defined(__FreeBSD__) && (__FreeBSD__ + 0) && ((__FreeBSD__ + 0) >= 5) # define FMTTIME FMTSZ"d" #else # define FMTTIME "ld" #endif /* * Define MXSemaHandle here so both vmmon and vmx see this definition. */ #ifdef _WIN32 typedef uintptr_t MXSemaHandle; #else typedef int MXSemaHandle; #endif /* * Define type for poll device handles. */ #ifdef _WIN32 typedef uintptr_t PollDevHandle; #else typedef int PollDevHandle; #endif /* * Define the utf16_t type. */ #if defined(_WIN32) && defined(_NATIVE_WCHAR_T_DEFINED) typedef wchar_t utf16_t; #else typedef uint16 utf16_t; #endif #endif /* _VM_BASIC_TYPES_H_ */ vmhgfs-only/vm_assert.h0000444000000000000000000002422012025726745014211 0ustar rootroot/********************************************************* * Copyright (C) 1998-2004 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 * *********************************************************/ /* * vm_assert.h -- * * The basic assertion facility for all VMware code. * * For proper use, see * http://vmweb.vmware.com/~mts/WebSite/guide/programming/asserts.html */ #ifndef _VM_ASSERT_H_ #define _VM_ASSERT_H_ #define INCLUDE_ALLOW_USERLEVEL #define INCLUDE_ALLOW_VMMEXT #define INCLUDE_ALLOW_MODULE #define INCLUDE_ALLOW_VMMON #define INCLUDE_ALLOW_VMNIXMOD #define INCLUDE_ALLOW_VMKERNEL #define INCLUDE_ALLOW_VMKDRIVERS #define INCLUDE_ALLOW_VMK_MODULE #define INCLUDE_ALLOW_DISTRIBUTE #define INCLUDE_ALLOW_VMCORE #define INCLUDE_ALLOW_VMIROM #include "includeCheck.h" // XXX not necessary except some places include vm_assert.h improperly #include "vm_basic_types.h" #include "vm_basic_defs.h" /* * XXX old file code */ #ifdef FILECODEINT #error "Don't define FILECODEINT. It is obsolete." #endif #ifdef FILECODE #error "Don't define FILECODE. It is obsolete." #endif /* * Panic and log functions */ EXTERN void Log(const char *fmt, ...) PRINTF_DECL(1, 2); EXTERN void Warning(const char *fmt, ...) PRINTF_DECL(1, 2); EXTERN NORETURN void Panic(const char *fmt, ...) PRINTF_DECL(1, 2); EXTERN void LogThrottled(uint32 *count, const char *fmt, ...) PRINTF_DECL(2, 3); EXTERN void WarningThrottled(uint32 *count, const char *fmt, ...) PRINTF_DECL(2, 3); /* DB family: messages which are parsed by logfile database system */ #define WarningDB Warning #define LogDB Log #define WarningThrottledDB WarningThrottled #define LogThrottledDB LogThrottled /* * Stress testing: redefine ASSERT_IFNOT() to taste */ #ifndef ASSERT_IFNOT #ifdef __cplusplus #define ASSERT_IFNOT(cond, panic) (UNLIKELY(!(cond)) ? (panic) : (void)0) #else #define ASSERT_IFNOT(cond, panic) (UNLIKELY(!(cond)) ? (panic) : 0) #endif #endif /* * Assert, panic, and log macros * * Some of these are redefined below undef !VMX86_DEBUG. * ASSERT() is special cased because of interaction with Windows DDK. */ #if defined VMX86_DEBUG || defined ASSERT_ALWAYS_AVAILABLE #undef ASSERT #define ASSERT(cond) \ ASSERT_IFNOT(cond, _ASSERT_PANIC(AssertAssert)) #endif #define ASSERT_BUG(bug, cond) \ ASSERT_IFNOT(cond, _ASSERT_PANIC_BUG(bug, AssertAssert)) #define ASSERT_BUG_DEBUGONLY(bug, cond) ASSERT_BUG(bug, cond) #define PANIC() _ASSERT_PANIC(AssertPanic) #define PANIC_BUG(bug) _ASSERT_PANIC_BUG(bug, AssertPanic) #define ASSERT_NOT_IMPLEMENTED(cond) \ ASSERT_IFNOT(cond, NOT_IMPLEMENTED()) #define ASSERT_NOT_IMPLEMENTED_BUG(bug, cond) \ ASSERT_IFNOT(cond, NOT_IMPLEMENTED_BUG(bug)) #define NOT_IMPLEMENTED() _ASSERT_PANIC(AssertNotImplemented) #define NOT_IMPLEMENTED_BUG(bug) _ASSERT_PANIC_BUG(bug, AssertNotImplemented) #define NOT_REACHED() _ASSERT_PANIC(AssertNotReached) #define NOT_REACHED_BUG(bug) _ASSERT_PANIC_BUG(bug, AssertNotReached) #define ASSERT_MEM_ALLOC(cond) \ ASSERT_IFNOT(cond, _ASSERT_PANIC(AssertMemAlloc)) #ifdef VMX86_DEVEL #define ASSERT_LENGTH(real, expected) \ ASSERT_IFNOT((real) == (expected), \ Panic(AssertLengthFmt, __FILE__, __LINE__, real, expected)) #else #define ASSERT_LENGTH(real, expected) ASSERT((real) == (expected)) #endif #ifdef VMX86_DEVEL #define ASSERT_DEVEL(cond) ASSERT(cond) #else #define ASSERT_DEVEL(cond) ((void) 0) #endif #define ASSERT_NO_INTERRUPTS() ASSERT(!INTERRUPTS_ENABLED()) #define ASSERT_HAS_INTERRUPTS() ASSERT(INTERRUPTS_ENABLED()) #define ASSERT_LOG_UNEXPECTED(bug, cond) \ (UNLIKELY(!(cond)) ? LOG_UNEXPECTED(bug) : 0) #ifdef VMX86_DEVEL #define LOG_UNEXPECTED(bug) \ Warning(AssertUnexpectedFmt, __FILE__, __LINE__, bug) #else #define LOG_UNEXPECTED(bug) \ Log(AssertUnexpectedFmt, __FILE__, __LINE__, bug) #endif #define ASSERT_NOT_TESTED(cond) (UNLIKELY(!(cond)) ? NOT_TESTED() : 0) #ifdef VMX86_DEVEL #define NOT_TESTED() Warning(AssertNotTestedFmt, __FILE__, __LINE__) #else #define NOT_TESTED() Log(AssertNotTestedFmt, __FILE__, __LINE__) #endif #define NOT_TESTED_ONCE() \ do { \ static Bool alreadyPrinted = FALSE; \ if (UNLIKELY(!alreadyPrinted)) { \ alreadyPrinted = TRUE; \ NOT_TESTED(); \ } \ } while (0) #define NOT_TESTED_1024() \ do { \ static uint16 count = 0; \ if (UNLIKELY(count == 0)) { NOT_TESTED(); } \ count = (count + 1) & 1023; \ } while (0) #define LOG_ONCE(_s) \ do { \ static Bool logged = FALSE; \ if (!logged) { \ Log _s; \ logged = TRUE; \ } \ } while (0) /* * Redefine macros that are only in debug versions */ #if !defined VMX86_DEBUG && !defined ASSERT_ALWAYS_AVAILABLE // { #undef ASSERT #define ASSERT(cond) ((void) 0) #undef ASSERT_BUG_DEBUGONLY #define ASSERT_BUG_DEBUGONLY(bug, cond) ((void) 0) #undef ASSERT_LENGTH #define ASSERT_LENGTH(real, expected) ((void) 0) /* * Expand NOT_REACHED() as appropriate for each situation. * * Mainly, we want the compiler to infer the same control-flow * information as it would from Panic(). Otherwise, different * compilation options will lead to different control-flow-derived * errors, causing some make targets to fail while others succeed. * * VC++ has the __assume() built-in function which we don't trust * (see bug 43485); gcc has no such construct; we just panic in * userlevel code. The monitor doesn't want to pay the size penalty * (measured at 212 bytes for the release vmm for a minimal infinite * loop; panic would cost even more) so it does without and lives * with the inconsistency. */ #ifdef VMM #undef NOT_REACHED #define NOT_REACHED() ((void) 0) #else // keep debug definition #endif #undef ASSERT_LOG_UNEXPECTED #define ASSERT_LOG_UNEXPECTED(bug, cond) ((void) 0) #undef LOG_UNEXPECTED #define LOG_UNEXPECTED(bug) ((void) 0) #undef ASSERT_NOT_TESTED #define ASSERT_NOT_TESTED(cond) ((void) 0) #undef NOT_TESTED #define NOT_TESTED() ((void) 0) #undef NOT_TESTED_ONCE #define NOT_TESTED_ONCE() ((void) 0) #undef NOT_TESTED_1024 #define NOT_TESTED_1024() ((void) 0) #endif // !VMX86_DEBUG } /* * Compile-time assertions. * * ASSERT_ON_COMPILE does not use the common * switch (0) { case 0: case (e): ; } trick because some compilers (e.g. MSVC) * generate code for it. * * The implementation uses both enum and typedef because the typedef alone is * insufficient; gcc allows arrays to be declared with non-constant expressions * (even in typedefs, where it makes no sense). */ #define ASSERT_ON_COMPILE(e) \ do { \ enum { AssertOnCompileMisused = ((e) ? 1 : -1) }; \ typedef char AssertOnCompileFailed[AssertOnCompileMisused]; \ } while (0) /* * To put an ASSERT_ON_COMPILE() outside a function, wrap it * in MY_ASSERTS(). The first parameter must be unique in * each .c file where it appears. For example, * * MY_ASSERTS(FS3_INT, * ASSERT_ON_COMPILE(sizeof(FS3_DiskLock) == 128); * ASSERT_ON_COMPILE(sizeof(FS3_DiskLockReserved) == DISK_BLOCK_SIZE); * ASSERT_ON_COMPILE(sizeof(FS3_DiskBlock) == DISK_BLOCK_SIZE); * ASSERT_ON_COMPILE(sizeof(Hardware_DMIUUID) == 16); * ) * * Caution: ASSERT() within MY_ASSERTS() is silently ignored. * The same goes for anything else not evaluated at compile time. */ #define MY_ASSERTS(name, assertions) \ static INLINE void name(void) { \ assertions \ } /* * Internal macros, functions, and strings * * The monitor wants to save space at call sites, so it has specialized * functions for each situation. User level wants to save on implementation * so it uses generic functions. */ #if !defined VMM || defined MONITOR_APP // { #define _ASSERT_PANIC(name) \ Panic(_##name##Fmt "\n", __FILE__, __LINE__) #define _ASSERT_PANIC_BUG(bug, name) \ Panic(_##name##Fmt " bugNr=%d\n", __FILE__, __LINE__, bug) #define AssertLengthFmt _AssertLengthFmt #define AssertUnexpectedFmt _AssertUnexpectedFmt #define AssertNotTestedFmt _AssertNotTestedFmt #endif // } // these don't have newline so a bug can be tacked on #define _AssertPanicFmt "PANIC %s:%d" #define _AssertAssertFmt "ASSERT %s:%d" #define _AssertNotImplementedFmt "NOT_IMPLEMENTED %s:%d" #define _AssertNotReachedFmt "NOT_REACHED %s:%d" #define _AssertMemAllocFmt "MEM_ALLOC %s:%d" // these are complete formats with newline #define _AssertLengthFmt "LENGTH %s:%d r=%#x e=%#x\n" #define _AssertUnexpectedFmt "UNEXPECTED %s:%d bugNr=%d\n" #define _AssertNotTestedFmt "NOT_TESTED %s:%d\n" #endif /* ifndef _VM_ASSERT_H_ */ vmhgfs-only/vmware_pack_init.h0000444000000000000000000000364412025726745015537 0ustar rootroot/********************************************************* * Copyright (C) 2002 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 * *********************************************************/ #ifndef __VMWARE_PACK_INIT_H__ # define __VMWARE_PACK_INIT_H__ /* * vmware_pack_init.h -- * * Platform-independent code to make the compiler pack (i.e. have them * occupy the smallest possible space) structure definitions. The following * constructs are known to work --hpreg * * #include "vmware_pack_begin.h" * struct foo { * ... * } * #include "vmware_pack_end.h" * ; * * typedef * #include "vmware_pack_begin.h" * struct foo { * ... * } * #include "vmware_pack_end.h" * foo; */ #ifdef _MSC_VER /* * MSVC 6.0 emits warning 4103 when the pack push and pop pragma pairing is * not balanced within 1 included file. That is annoying because our scheme * is based on the pairing being balanced between 2 included files. * * So we disable this warning, but this is safe because the compiler will also * emit warning 4161 when there is more pops than pushes within 1 main * file --hpreg */ # pragma warning(disable:4103) #elif __GNUC__ #else # error Compiler packing... #endif #endif /* __VMWARE_PACK_INIT_H__ */ vmhgfs-only/vmware_pack_begin.h0000444000000000000000000000244412025726745015655 0ustar rootroot/********************************************************* * Copyright (C) 2002 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 * *********************************************************/ /* * vmware_pack_begin.h -- * * Begin of structure packing. See vmware_pack_init.h for details. * * Note that we do not use the following construct in this include file, * because we want to emit the code every time the file is included --hpreg * * #ifndef foo * # define foo * ... * #endif * */ #include "vmware_pack_init.h" #ifdef _MSC_VER # pragma pack(push, 1) #elif __GNUC__ #else # error Compiler packing... #endif vmhgfs-only/vmware_pack_end.h0000444000000000000000000000247012025726745015336 0ustar rootroot/********************************************************* * Copyright (C) 2002 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 * *********************************************************/ /* * vmware_pack_end.h -- * * End of structure packing. See vmware_pack_init.h for details. * * Note that we do not use the following construct in this include file, * because we want to emit the code every time the file is included --hpreg * * #ifndef foo * # define foo * ... * #endif * */ #include "vmware_pack_init.h" #ifdef _MSC_VER # pragma pack(pop) #elif __GNUC__ __attribute__((__packed__)) #else # error Compiler packing... #endif vmhgfs-only/includeCheck.h0000444000000000000000000001024612025726745014572 0ustar rootroot/********************************************************* * Copyright (C) 1998 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 * *********************************************************/ /* * includeCheck.h -- * * Restrict include file use. * * In every .h file, define one or more of these * * INCLUDE_ALLOW_VMX * INCLUDE_ALLOW_USERLEVEL * INCLUDE_ALLOW_VMMEXT * INCLUDE_ALLOW_VMCORE * INCLUDE_ALLOW_MODULE * INCLUDE_ALLOW_VMNIXMOD * INCLUDE_ALLOW_VMKERNEL * INCLUDE_ALLOW_DISTRIBUTE * INCLUDE_ALLOW_VMK_MODULE * INCLUDE_ALLOW_VMKDRIVERS * INCLUDE_ALLOW_VMIROM * * Then include this file. * * Any file that has INCLUDE_ALLOW_DISTRIBUTE defined will potentially * be distributed in source form along with GPLed code. Ensure * that this is acceptable. */ /* * Declare a VMCORE-only variable to help classify object * files. The variable goes in the common block and does * not create multiple definition link-time conflicts. */ #if defined VMCORE && defined VMX86_DEVEL && defined VMX86_DEBUG && \ defined linux && !defined MODULE && \ !defined COMPILED_WITH_VMCORE #define COMPILED_WITH_VMCORE compiled_with_vmcore #ifdef ASM .comm compiled_with_vmcore, 0 #else asm(".comm compiled_with_vmcore, 0"); #endif /* ASM */ #endif #if defined VMCORE && \ !(defined VMX86_VMX || defined VMM || \ defined MONITOR_APP || defined VMMON) #error "Makefile problem: VMCORE without VMX86_VMX or \ VMM or MONITOR_APP or MODULE." #endif #if defined VMCORE && !defined INCLUDE_ALLOW_VMCORE #error "The surrounding include file is not allowed in vmcore." #endif #undef INCLUDE_ALLOW_VMCORE #if defined VMX86_VMX && !defined VMCORE && \ !(defined INCLUDE_ALLOW_VMX || defined INCLUDE_ALLOW_USERLEVEL) #error "The surrounding include file is not allowed in the VMX." #endif #undef INCLUDE_ALLOW_VMX #if defined USERLEVEL && !defined VMX86_VMX && !defined VMCORE && \ !defined INCLUDE_ALLOW_USERLEVEL #error "The surrounding include file is not allowed at userlevel." #endif #undef INCLUDE_ALLOW_USERLEVEL #if defined VMM && !defined VMCORE && \ !defined INCLUDE_ALLOW_VMMEXT #error "The surrounding include file is not allowed in the monitor." #endif #undef INCLUDE_ALLOW_VMMEXT #if defined MODULE && !defined VMKERNEL_MODULE && !defined VMNIXMOD && \ !defined VMMON && !defined INCLUDE_ALLOW_MODULE #error "The surrounding include file is not allowed in driver modules." #endif #undef INCLUDE_ALLOW_MODULE #if defined VMMON && !defined INCLUDE_ALLOW_VMMON #error "The surrounding include file is not allowed in vmmon." #endif #undef INCLUDE_ALLOW_VMMON #if defined VMKERNEL && !defined INCLUDE_ALLOW_VMKERNEL #error "The surrounding include file is not allowed in the vmkernel." #endif #undef INCLUDE_ALLOW_VMKERNEL #if defined GPLED_CODE && !defined INCLUDE_ALLOW_DISTRIBUTE #error "The surrounding include file is not allowed in GPL code." #endif #undef INCLUDE_ALLOW_DISTRIBUTE #if defined VMKERNEL_MODULE && !defined VMKERNEL && \ !defined INCLUDE_ALLOW_VMK_MODULE && !defined INCLUDE_ALLOW_VMKDRIVERS #error "The surrounding include file is not allowed in vmkernel modules." #endif #undef INCLUDE_ALLOW_VMK_MODULE #undef INCLUDE_ALLOW_VMKDRIVERS #if defined VMNIXMOD && !defined INCLUDE_ALLOW_VMNIXMOD #ifndef VMNIXMOD_VM #error "The surrounding include file is not allowed in vmnixmod." #endif #endif #undef INCLUDE_ALLOW_VMNIXMOD #if defined VMIROM && ! defined INCLUDE_ALLOW_VMIROM #error "The surrounding include file is not allowed in vmirom." #endif #undef INCLUDE_ALLOW_VMIROM vmhgfs-only/vm_basic_asm.h0000444000000000000000000004244112025726745014636 0ustar rootroot/********************************************************* * Copyright (C) 2003 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 * *********************************************************/ /* * vm_basic_asm.h * * Basic asm macros */ #ifndef _VM_BASIC_ASM_H_ #define _VM_BASIC_ASM_H_ #define INCLUDE_ALLOW_USERLEVEL #define INCLUDE_ALLOW_VMMEXT #define INCLUDE_ALLOW_MODULE #define INCLUDE_ALLOW_VMMON #define INCLUDE_ALLOW_VMNIXMOD #define INCLUDE_ALLOW_VMK_MODULE #define INCLUDE_ALLOW_VMKERNEL #define INCLUDE_ALLOW_DISTRIBUTE #define INCLUDE_ALLOW_VMCORE #define INCLUDE_ALLOW_VMIROM #include "includeCheck.h" #include "vm_basic_types.h" #include "x86cpuid.h" #ifdef VM_X86_64 #include "vm_basic_asm_x86_64.h" #else #include "vm_basic_asm_x86.h" #endif /* * x86-64 windows doesn't support inline asm so we have to use these * intrinsic functions defined in the compiler. Not all of these are well * documented. There is an array in the compiler dll (c1.dll) which has * an array of the names of all the intrinsics minus the leading * underscore. Searching around in the ntddk.h file can also be helpful. * * The declarations for the intrinsic functions were taken from the DDK. * Our declarations must match the ddk's otherwise the 64-bit c++ compiler * will complain about second linkage of the intrinsic functions. * We define the intrinsic using the basic types corresponding to the * Windows typedefs. This avoids having to include windows header files * to get to the windows types. */ #ifdef _MSC_VER #ifdef __cplusplus extern "C" { #endif /* * It seems x86 & x86-64 windows still implements these intrinsic * functions. The documentation for the x86-64 suggest the * __inbyte/__outbyte intrinsics eventhough the _in/_out work fine and * __inbyte/__outbyte aren't supported on x86. */ int _inp(unsigned short); unsigned short _inpw(unsigned short); unsigned long _inpd(unsigned short); int _outp(unsigned short, int); unsigned short _outpw(unsigned short, unsigned short); unsigned long _outpd(uint16, unsigned long); #pragma intrinsic(_inp, _inpw, _inpd, _outp, _outpw, _outpw, _outpd) void _ReadWriteBarrier(void); #pragma intrinsic(_ReadWriteBarrier) #ifdef VM_X86_64 /* * intrinsic functions only supported by x86-64 windows as of 2k3sp1 */ void __cpuid(unsigned int*, unsigned int); unsigned __int64 __rdtsc(void); void __stosw(unsigned short*, unsigned short, size_t); void __stosd(unsigned long*, unsigned long, size_t); #pragma intrinsic(__cpuid, __rdtsc, __stosw, __stosd) /* * intrinsic functions supported by x86-64 windows and newer x86 * compilers (13.01.2035 for _BitScanForward). */ unsigned char _BitScanForward(unsigned long*, unsigned long); void _mm_pause(void); #pragma intrinsic(_BitScanForward, _mm_pause) #endif /* VM_X86_64 */ #ifdef __cplusplus } #endif #endif /* _MSC_VER */ #ifdef __GNUC__ // { /* * Checked against the Intel manual and GCC --hpreg * * volatile because reading from port can modify the state of the underlying * hardware. * * Note: The undocumented %z construct doesn't work (internal compiler error) * with gcc-2.95.1 */ #define __GCC_IN(s, type, name) \ static INLINE type \ name(uint16 port) \ { \ type val; \ \ __asm__ __volatile__( \ "in" #s " %w1, %0" \ : "=a" (val) \ : "Nd" (port) \ ); \ \ return val; \ } __GCC_IN(b, uint8, INB) __GCC_IN(w, uint16, INW) __GCC_IN(l, uint32, IN32) /* * Checked against the Intel manual and GCC --hpreg * * Note: The undocumented %z construct doesn't work (internal compiler error) * with gcc-2.95.1 */ #define __GCC_OUT(s, s2, port, val) do { \ __asm__( \ "out" #s " %" #s2 "1, %w0" \ : \ : "Nd" (port), "a" (val) \ ); \ } while (0) #define OUTB(port, val) __GCC_OUT(b, b, port, val) #define OUTW(port, val) __GCC_OUT(w, w, port, val) #define OUT32(port, val) __GCC_OUT(l, , port, val) #define GET_CURRENT_EIP(_eip) \ __asm__ __volatile("call 0\n\tpopl %0" : "=r" (_eip): ); /* * Checked against the Intel manual and GCC --hpreg * * Need __volatile__ and "memory" since CPUID has a synchronizing effect. * The CPUID may also change at runtime (APIC flag, etc). * */ static INLINE void __GET_CPUID(int eax, // IN CPUIDRegs *regs) // OUT { __asm__ __volatile__( #if defined __PIC__ && !vm_x86_64 // %ebx is reserved by the compiler. "movl %%ebx, %1" "\n\t" "cpuid" "\n\t" "xchgl %%ebx, %1" : "=a" (regs->eax), "=&rm" (regs->ebx), "=c" (regs->ecx), "=d" (regs->edx) #else "cpuid" : "=a" (regs->eax), "=b" (regs->ebx), "=c" (regs->ecx), "=d" (regs->edx) #endif : "a" (eax) : "memory" ); } static INLINE void __GET_CPUID2(int eax, // IN int ecx, // IN CPUIDRegs *regs) // OUT { __asm__ __volatile__( #if defined __PIC__ && !vm_x86_64 // %ebx is reserved by the compiler. "movl %%ebx, %1" "\n\t" "cpuid" "\n\t" "xchgl %%ebx, %1" : "=a" (regs->eax), "=&rm" (regs->ebx), "=c" (regs->ecx), "=d" (regs->edx) #else "cpuid" : "=a" (regs->eax), "=b" (regs->ebx), "=c" (regs->ecx), "=d" (regs->edx) #endif : "a" (eax), "c" (ecx) : "memory" ); } static INLINE uint32 __GET_EAX_FROM_CPUID(int eax) // IN { #if defined __PIC__ && !vm_x86_64 // %ebx is reserved by the compiler. uint32 ebx; __asm__ __volatile__( "movl %%ebx, %1" "\n\t" "cpuid" "\n\t" "xchgl %%ebx, %1" : "=a" (eax), "=&rm" (ebx) : "a" (eax) : "memory", "%ecx", "%edx" ); #else __asm__ __volatile__( "cpuid" : "=a" (eax) : "a" (eax) : "memory", "%ebx", "%ecx", "%edx" ); #endif return eax; } static INLINE uint32 __GET_EBX_FROM_CPUID(int eax) // IN { uint32 ebx; __asm__ __volatile__( #if defined __PIC__ && !vm_x86_64 // %ebx is reserved by the compiler. "movl %%ebx, %1" "\n\t" "cpuid" "\n\t" "xchgl %%ebx, %1" : "=a" (eax), "=&rm" (ebx) #else "cpuid" : "=a" (eax), "=b" (ebx) #endif : "a" (eax) : "memory", "%ecx", "%edx" ); return ebx; } static INLINE uint32 __GET_ECX_FROM_CPUID(int eax) // IN { uint32 ecx; #if defined __PIC__ && !vm_x86_64 // %ebx is reserved by the compiler. uint32 ebx; __asm__ __volatile__( "movl %%ebx, %1" "\n\t" "cpuid" "\n\t" "xchgl %%ebx, %1" : "=a" (eax), "=&rm" (ebx), "=c" (ecx) : "a" (eax) : "memory", "%edx" ); #else __asm__ __volatile__( "cpuid" : "=a" (eax), "=c" (ecx) : "a" (eax) : "memory", "%ebx", "%edx" ); #endif return ecx; } static INLINE uint32 __GET_EDX_FROM_CPUID(int eax) // IN { uint32 edx; #if defined __PIC__ && !vm_x86_64 // %ebx is reserved by the compiler. uint32 ebx; __asm__ __volatile__( "movl %%ebx, %1" "\n\t" "cpuid" "\n\t" "xchgl %%ebx, %1" : "=a" (eax), "=&rm" (ebx), "=d" (edx) : "a" (eax) : "memory", "%ecx" ); #else __asm__ __volatile__( "cpuid" : "=a" (eax), "=d" (edx) : "a" (eax) : "memory", "%ebx", "%ecx" ); #endif return edx; } static INLINE uint32 __GET_EAX_FROM_CPUID4(int ecx) // IN { uint32 eax; #if defined __PIC__ && !vm_x86_64 // %ebx is reserved by the compiler. uint32 ebx; __asm__ __volatile__( "movl %%ebx, %1" "\n\t" "cpuid" "\n\t" "xchgl %%ebx, %1" : "=a" (eax), "=&rm" (ebx), "=c" (ecx) : "a" (4), "c" (ecx) : "memory", "%edx" ); #else __asm__ __volatile__( "cpuid" : "=a" (eax), "=c" (ecx) : "a" (4), "c" (ecx) : "memory", "%ebx", "%edx" ); #endif return eax; } #elif defined(_MSC_VER) // } { static INLINE uint8 INB(uint16 port) { return (uint8)_inp(port); } static INLINE void OUTB(uint16 port, uint8 value) { _outp(port, value); } static INLINE uint16 INW(uint16 port) { return _inpw(port); } static INLINE void OUTW(uint16 port, uint16 value) { _outpw(port, value); } static INLINE uint32 IN32(uint16 port) { return _inpd(port); } static INLINE void OUT32(uint16 port, uint32 value) { _outpd(port, value); } #ifndef VM_X86_64 #ifdef NEAR #undef NEAR #endif #define GET_CURRENT_EIP(_eip) do { \ __asm call NEAR PTR $+5 \ __asm pop eax \ __asm mov _eip, eax \ } while (0) #endif static INLINE void __GET_CPUID(int input, CPUIDRegs *regs) { #ifdef VM_X86_64 __cpuid((unsigned int *)regs, input); #else __asm push esi __asm push ebx __asm push ecx __asm push edx __asm mov eax, input __asm mov esi, regs __asm _emit 0x0f __asm _emit 0xa2 __asm mov 0x0[esi], eax __asm mov 0x4[esi], ebx __asm mov 0x8[esi], ecx __asm mov 0xC[esi], edx __asm pop edx __asm pop ecx __asm pop ebx __asm pop esi #endif } #ifdef VM_X86_64 /* * No inline assembly in Win64. Implemented in bora/lib/user in * cpuidMasm64.asm. */ extern void __GET_CPUID2(int inputEax, int inputEcx, CPUIDRegs *regs); #else // VM_X86_64 static INLINE void __GET_CPUID2(int inputEax, int inputEcx, CPUIDRegs *regs) { __asm push esi __asm push ebx __asm push ecx __asm push edx __asm mov eax, inputEax __asm mov ecx, inputEcx __asm mov esi, regs __asm _emit 0x0f __asm _emit 0xa2 __asm mov 0x0[esi], eax __asm mov 0x4[esi], ebx __asm mov 0x8[esi], ecx __asm mov 0xC[esi], edx __asm pop edx __asm pop ecx __asm pop ebx __asm pop esi } #endif static INLINE uint32 __GET_EAX_FROM_CPUID(int input) { #ifdef VM_X86_64 CPUIDRegs regs; __cpuid((unsigned int *)®s, input); return regs.eax; #else uint32 output; //NOT_TESTED(); __asm push ebx __asm push ecx __asm push edx __asm mov eax, input __asm _emit 0x0f __asm _emit 0xa2 __asm mov output, eax __asm pop edx __asm pop ecx __asm pop ebx return output; #endif } static INLINE uint32 __GET_EBX_FROM_CPUID(int input) { #ifdef VM_X86_64 CPUIDRegs regs; __cpuid((unsigned int *)®s, input); return regs.ebx; #else uint32 output; //NOT_TESTED(); __asm push ebx __asm push ecx __asm push edx __asm mov eax, input __asm _emit 0x0f __asm _emit 0xa2 __asm mov output, ebx __asm pop edx __asm pop ecx __asm pop ebx return output; #endif } static INLINE uint32 __GET_ECX_FROM_CPUID(int input) { #ifdef VM_X86_64 CPUIDRegs regs; __cpuid((unsigned int *)®s, input); return regs.ecx; #else uint32 output; //NOT_TESTED(); __asm push ebx __asm push ecx __asm push edx __asm mov eax, input __asm _emit 0x0f __asm _emit 0xa2 __asm mov output, ecx __asm pop edx __asm pop ecx __asm pop ebx return output; #endif } static INLINE uint32 __GET_EDX_FROM_CPUID(int input) { #ifdef VM_X86_64 CPUIDRegs regs; __cpuid((unsigned int *)®s, input); return regs.edx; #else uint32 output; //NOT_TESTED(); __asm push ebx __asm push ecx __asm push edx __asm mov eax, input __asm _emit 0x0f __asm _emit 0xa2 __asm mov output, edx __asm pop edx __asm pop ecx __asm pop ebx return output; #endif } #ifdef VM_X86_64 /* * No inline assembly in Win64. Implemented in bora/lib/user in * cpuidMasm64.asm. */ extern uint32 __GET_EAX_FROM_CPUID4(int inputEcx); #else // VM_X86_64 static INLINE uint32 __GET_EAX_FROM_CPUID4(int inputEcx) { uint32 output; //NOT_TESTED(); __asm push ebx __asm push ecx __asm push edx __asm mov eax, 4 __asm mov ecx, inputEcx __asm _emit 0x0f __asm _emit 0xa2 __asm mov output, eax __asm pop edx __asm pop ecx __asm pop ebx return output; } #endif // VM_X86_64 #else // } #error #endif #define CPUID_FOR_SIDE_EFFECTS() ((void)__GET_EAX_FROM_CPUID(0)) static INLINE void __GET_CPUID4(int inputEcx, CPUIDRegs *regs) { __GET_CPUID2(4, inputEcx, regs); } /* The first parameter is used as an rvalue and then as an lvalue. */ #define GET_CPUID(_ax, _bx, _cx, _dx) { \ CPUIDRegs regs; \ __GET_CPUID(_ax, ®s); \ _ax = regs.eax; \ _bx = regs.ebx; \ _cx = regs.ecx; \ _dx = regs.edx; \ } /* Sequence recommended by Intel for the Pentium 4. */ #define INTEL_MICROCODE_VERSION() ( \ __SET_MSR(MSR_BIOS_SIGN_ID, 0), \ __GET_EAX_FROM_CPUID(1), \ __GET_MSR(MSR_BIOS_SIGN_ID)) #ifdef _MSC_VER static INLINE int ffs(uint32 bitVector) { int idx; if (!bitVector) { return 0; } #ifdef VM_X86_64 _BitScanForward((unsigned long*)&idx, (unsigned long)bitVector); #else __asm bsf eax, bitVector __asm mov idx, eax #endif return idx+1; } #endif #ifdef __GNUC__ static INLINE void * uint16set(void *dst, uint16 val, size_t count) { int dummy0; int dummy1; __asm__ __volatile__("\t" "cld" "\n\t" "rep ; stosw" "\n" : "=c" (dummy0), "=D" (dummy1) : "0" (count), "1" (dst), "a" (val) : "memory", "cc" ); return dst; } static INLINE void * uint32set(void *dst, uint32 val, size_t count) { int dummy0; int dummy1; __asm__ __volatile__("\t" "cld" "\n\t" "rep ; stosl" "\n" : "=c" (dummy0), "=D" (dummy1) : "0" (count), "1" (dst), "a" (val) : "memory", "cc" ); return dst; } #elif defined(_MSC_VER) static INLINE void * uint16set(void *dst, uint16 val, size_t count) { #ifdef VM_X86_64 __stosw((uint16*)dst, val, count); #else __asm { pushf; mov ax, val; mov ecx, count; mov edi, dst; cld; rep stosw; popf; } #endif return dst; } static INLINE void * uint32set(void *dst, uint32 val, size_t count) { #ifdef VM_X86_64 __stosd((unsigned long*)dst, (unsigned long)val, count); #else __asm { pushf; mov eax, val; mov ecx, count; mov edi, dst; cld; rep stosd; popf; } #endif return dst; } #else #error "No compiler defined for uint*set" #endif /* *----------------------------------------------------------------------------- * * Bswap -- * * Swap the 4 bytes of "v" as follows: 3210 -> 0123. * *----------------------------------------------------------------------------- */ #ifdef __GNUC__ // { static INLINE uint32 Bswap(uint32 v) { /* Checked against the Intel manual and GCC --hpreg */ __asm__( "bswap %0" : "=r" (v) : "0" (v) ); return v; } #endif // } #ifdef __GNUC__ // { /* * COMPILER_MEM_BARRIER prevents the compiler from re-ordering memory * references accross the barrier. NOTE: It does not generate any * instruction, so the CPU is free to do whatever it wants to... */ #define COMPILER_MEM_BARRIER() __asm__ __volatile__ ("": : :"memory") #elif defined(_MSC_VER) // } { #define COMPILER_MEM_BARRIER() _ReadWriteBarrier() #endif // } /* * PAUSE is a P4 instruction that improves spinlock power+performance; * on non-P4 IA32 systems, the encoding is interpreted as a REPZ-NOP. * Use volatile to avoid NOP removal. */ static INLINE void PAUSE(void) #ifdef __GNUC__ { __asm__ __volatile__( "pause" :); } #elif defined(_MSC_VER) #ifdef VM_X86_64 { _mm_pause(); } #else /* VM_X86_64 */ #pragma warning( disable : 4035) { __asm _emit 0xf3 __asm _emit 0x90 } #pragma warning (default: 4035) #endif /* VM_X86_64 */ #else /* __GNUC__ */ #error No compiler defined for PAUSE #endif /* * Checked against the Intel manual and GCC --hpreg * * volatile because the tsc always changes without the compiler knowing it. */ static INLINE uint64 RDTSC(void) #ifdef __GNUC__ { #ifdef VM_X86_64 uint64 tscLow; uint64 tscHigh; __asm__ __volatile__( "rdtsc" : "=a" (tscLow), "=d" (tscHigh) ); return tscHigh << 32 | tscLow; #else uint64 tim; __asm__ __volatile__( "rdtsc" : "=A" (tim) ); return tim; #endif } #elif defined(_MSC_VER) #ifdef VM_X86_64 { return __rdtsc(); } #else #pragma warning( disable : 4035) { __asm _emit 0x0f __asm _emit 0x31 } #pragma warning (default: 4035) #endif /* VM_X86_64 */ #else /* __GNUC__ */ #error No compiler defined for RDTSC #endif /* __GNUC__ */ #endif vmhgfs-only/vm_basic_asm_x86.h0000444000000000000000000003736112025726745015350 0ustar rootroot/********************************************************* * Copyright (C) 1998-2003 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 * *********************************************************/ /* * vm_basic_asm_x86.h * * Basic IA32 asm macros */ #ifndef _VM_BASIC_ASM_X86_H_ #define _VM_BASIC_ASM_X86_H_ #define INCLUDE_ALLOW_USERLEVEL #define INCLUDE_ALLOW_VMMEXT #define INCLUDE_ALLOW_MODULE #define INCLUDE_ALLOW_VMMON #define INCLUDE_ALLOW_VMNIXMOD #define INCLUDE_ALLOW_VMK_MODULE #define INCLUDE_ALLOW_VMKERNEL #define INCLUDE_ALLOW_DISTRIBUTE #define INCLUDE_ALLOW_VMCORE #define INCLUDE_ALLOW_VMIROM #include "includeCheck.h" #ifdef VM_X86_64 /* * The gcc inline asm uses the "A" constraint which differs in 32 & 64 * bit mode. 32 bit means eax and edx, 64 means rax or rdx. */ #error "x86-64 not supported" #endif /* * from linux: usr/include/asm/io.h */ #ifdef __GNUC__ #ifndef __SLOW_DOWN_IO #ifdef SLOW_IO_BY_JUMPING #define __SLOW_DOWN_IO __asm__ __volatile__("jmp 1f\n1:\tjmp 1f\n1:") #else #define __SLOW_DOWN_IO __asm__ __volatile__("outb %al,$0x80") #endif #endif #elif _MSC_VER #ifdef SLOW_IO_BY_JUMPING #define __SLOW_DOWN_IO __asm jmp SHORT $+2 __asm jmp SHORT $+2 #else #define __SLOW_DOWN_IO __asm out 80h,al #endif #else #error #endif #ifdef REALLY_SLOW_IO #define SLOW_DOWN_IO { __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; } #else #define SLOW_DOWN_IO __SLOW_DOWN_IO #endif /* * FXSAVE/FXRSTOR * save/restore SIMD/MMX fpu state * * The pointer passed in must be 16-byte aligned. * * Intel and AMD processors behave differently w.r.t. fxsave/fxrstor. Intel * processors unconditionally save the exception pointer state (instruction * ptr., data ptr., and error instruction opcode). FXSAVE_ES1 and FXRSTOR_ES1 * work correctly for Intel processors. * * AMD processors only save the exception pointer state if ES=1. This leads to a * security hole whereby one process/VM can inspect the state of another process * VM. The AMD recommended workaround involves clobbering the exception pointer * state unconditionally, and this is implemented in FXRSTOR_AMD_ES0. Note that * FXSAVE_ES1 will only save the exception pointer state for AMD processors if * ES=1. * * The workaround (FXRSTOR_AMD_ES0) only costs 1 cycle more than just doing an * fxrstor, on both AMD Opteron and Intel Core CPUs. */ #if defined(__GNUC__) static INLINE void FXSAVE_ES1(uint8 *save) { __asm__ __volatile__ ("fxsave %0\n" : "=m" (*save) : : "memory"); } static INLINE void FXRSTOR_ES1(const uint8 *load) { __asm__ __volatile__ ("fxrstor %0\n" : : "m" (*load) : "memory"); } static INLINE void FXRSTOR_AMD_ES0(const uint8 *load) { uint64 dummy = 0; __asm__ __volatile__ ("fnstsw %%ax \n" // Grab x87 ES bit "bt $7,%%ax \n" // Test ES bit "jnc 1f \n" // Jump if ES=0 "fnclex \n" // ES=1. Clear it so fild doesn't trap "1: \n" "ffree %%st(7) \n" // Clear tag bit - avoid poss. stack overflow "fildl %0 \n" // Dummy Load from "safe address" changes all // x87 exception pointers. "fxrstor %1 \n" : : "m" (dummy), "m" (*load) : "ax", "memory"); } #endif /* __GNUC__ */ /* *----------------------------------------------------------------------------- * * Div643232 -- * * Unsigned integer division: * The dividend is 64-bit wide * The divisor is 32-bit wide * The quotient is 32-bit wide * * Use this function if you are certain that: * o Either the quotient will fit in 32 bits, * o Or your code is ready to handle a #DE exception indicating overflow. * If that is not the case, then use Div643264(). --hpreg * * Results: * Quotient and remainder * * Side effects: * None * *----------------------------------------------------------------------------- */ #if defined(__GNUC__) static INLINE void Div643232(uint64 dividend, // IN uint32 divisor, // IN uint32 *quotient, // OUT uint32 *remainder) // OUT { /* Checked against the Intel manual and GCC --hpreg */ __asm__( "divl %4" : "=a" (*quotient), "=d" (*remainder) : "0" ((uint32)dividend), "1" ((uint32)(dividend >> 32)), "rm" (divisor) : "cc" ); } #elif _MSC_VER static INLINE void Div643232(uint64 dividend, // IN uint32 divisor, // IN uint32 *quotient, // OUT uint32 *remainder) // OUT { /* Written and tested by mann, checked by dbudko and hpreg */ __asm { mov eax, DWORD PTR [dividend] mov edx, DWORD PTR [dividend+4] div DWORD PTR [divisor] mov edi, DWORD PTR [quotient] mov [edi], eax mov edi, DWORD PTR [remainder] mov [edi], edx } } #else #error No compiler defined for Div643232 #endif #if defined(__GNUC__) /* *----------------------------------------------------------------------------- * * Div643264 -- * * Unsigned integer division: * The dividend is 64-bit wide * The divisor is 32-bit wide * The quotient is 64-bit wide --hpreg * * Results: * Quotient and remainder * * Side effects: * None * *----------------------------------------------------------------------------- */ static INLINE void Div643264(uint64 dividend, // IN uint32 divisor, // IN uint64 *quotient, // OUT uint32 *remainder) // OUT { uint32 hQuotient; uint32 lQuotient; /* Checked against the Intel manual and GCC --hpreg */ __asm__( "divl %5" "\n\t" "movl %%eax, %0" "\n\t" "movl %4, %%eax" "\n\t" "divl %5" : "=&rm" (hQuotient), "=a" (lQuotient), "=d" (*remainder) : "1" ((uint32)(dividend >> 32)), "g" ((uint32)dividend), "rm" (divisor), "2" (0) : "cc" ); *quotient = (uint64)hQuotient << 32 | lQuotient; } #endif /* *----------------------------------------------------------------------------- * * Mul64x3264 -- * * Unsigned integer by fixed point multiplication: * Unsigned 64-bit integer multiplicand. * Unsigned 32-bit fixed point multiplier, represented as * multiplier >> shift, where shift < 64. * Unsigned 64-bit integer product. * * Implementation: * Multiply 64x32 bits to yield a full 96-bit product. * Shift right by shift. * Return the low-order 64 bits of the result. * * Result: * Product * * Side effects: * None * *----------------------------------------------------------------------------- */ #if defined(__GNUC__) static INLINE uint64 Mul64x3264(uint64 multiplicand, uint32 multiplier, uint32 shift) { uint64 result; uint32 tmp1, tmp2; // ASSERT(shift >= 0 && shift < 64); /* * Written and tested by mann, improved with suggestions by hpreg. * * The main improvement over the previous version is that the test * of shift against 32 is moved out of the asm and into C code. * This lets the compiler delete the test and one of the * alternative code sequences in the case where shift is a * constant. It also lets us use the best code sequence in each * alternative, rather than a compromise. The downside is that in * the non-constant case, this version takes slightly more code * space. * * Note on the constraints: We don't really want multiplicand to * start in %edx:%eax as the =A constraint dictates; in fact, we'd * prefer any *other* two registers. But gcc doesn't have * constraint syntax for any other register pair, and trying to * constrain ((uint32) multiplicand) to one place and (multiplicand * >> 32) to another generates *really* bad code -- gcc is just not * smart enough, at least in the version we are currently using. */ if (shift < 32) { asm("mov %%eax, %2 \n\t" // Save lo(multiplicand) in tmp2 "mov %%edx, %%eax \n\t" // Get hi(multiplicand) "mull %4 \n\t" // p2 = hi(multiplicand) * multiplier "xchg %%eax, %2 \n\t" // Save lo(p2) in tmp2, get lo(multiplicand) "mov %%edx, %1 \n\t" // Save hi(p2) in tmp1 "mull %4 \n\t" // p1 = lo(multiplicand) * multiplier "addl %2, %%edx \n\t" // hi(p1) += lo(p2) "adcl $0, %1 \n\t" // hi(p2) += carry from previous step "shrdl %%edx, %%eax \n\t" // result = hi(p2):hi(p1):lo(p1) >> shift "shrdl %1, %%edx" : "=A" (result), "=&r" (tmp1), // use in shrdl requires it to be a register "=&r" (tmp2) // could be "=&rm" but "m" is slower : "0" (multiplicand), "rm" (multiplier), "c" (shift) : "cc" ); } else { asm("mov %%edx, %2 \n\t" // Save hi(multiplicand) in tmp2 "mull %4 \n\t" // p1 = lo(multiplicand) * multiplier "mov %%edx, %1 \n\t" // Save hi(p1) in tmp1 "mov %2, %%eax \n\t" // Discard lo(p1), get hi(multiplicand) "mull %4 \n\t" // p2 = hi(multiplicand) * multiplier "addl %1, %%eax \n\t" // lo(p2) += hi(p1) "adcl $0, %%edx \n\t" // hi(p2) += carry from previous step "shrdl %%edx, %%eax \n\t" // result = p2 >> (shift & 31) "shrl %%cl, %%edx" : "=A" (result), "=&r" (tmp1), // could be "=&rm" but "m" is slower "=&r" (tmp2) // could be "=&rm" but "m" is slower : "0" (multiplicand), "rm" (multiplier), "c" (shift) : "cc" ); } return result; } #elif _MSC_VER #pragma warning(disable: 4035) static INLINE uint64 Mul64x3264(uint64 multiplicand, uint32 multiplier, uint32 shift) { // ASSERT(shift >= 0 && shift < 64); /* Written and tested by mann, checked by dbudko and hpreg */ __asm { mov eax, DWORD PTR [multiplicand+4] // Get hi(multiplicand) mul DWORD PTR [multiplier] // p2 = hi(multiplicand) * multiplier mov ecx, eax // Save lo(p2) mov ebx, edx // Save hi(p2) mov eax, DWORD PTR [multiplicand] // Get lo(multiplicand) mul DWORD PTR [multiplier+0] // p1 = lo(multiplicand) * multiplier add edx, ecx // hi(p1) += lo(p2) adc ebx, 0 // hi(p2) += carry from previous step mov ecx, DWORD PTR [shift] // Get shift cmp ecx, 32 // shift < 32? jl SHORT l2 // Go if so mov eax, edx // result = hi(p2):hi(p1) >> (shift & 31) mov edx, ebx shrd eax, edx, cl shr edx, cl jmp SHORT l3 l2: shrd eax, edx, cl // result = hi(p2):hi(p1):lo(p1) >> shift shrd edx, ebx, cl l3: } // return with result in edx:eax } #pragma warning(default: 4035) #else #error No compiler defined for Mul64x3264 #endif /* *----------------------------------------------------------------------------- * * Muls64x32s64 -- * * Signed integer by fixed point multiplication: * Signed 64-bit integer multiplicand. * Unsigned 32-bit fixed point multiplier, represented as * multiplier >> shift, where shift < 64. * Signed 64-bit integer product. * * Implementation: * Multiply 64x32 bits to yield a full 96-bit product. * Shift right by the location of the binary point. * Return the low-order 64 bits of the result. * * Result: * Product * * Side effects: * None * *----------------------------------------------------------------------------- */ #if defined(__GNUC__) static INLINE int64 Muls64x32s64(int64 multiplicand, uint32 multiplier, uint32 shift) { int64 result; uint32 tmp1, tmp2; // ASSERT(shift >= 0 && shift < 64); /* Written and tested by mann, checked by dbudko and hpreg */ /* XXX hpreg suggested some improvements that we haven't converged on yet */ asm("mov %%eax, %2\n\t" // Save lo(multiplicand) "mov %%edx, %%eax\n\t" // Get hi(multiplicand) "test %%eax, %%eax\n\t" // Check sign of multiplicand "jl 0f\n\t" // Go if negative "mull %4\n\t" // p2 = hi(multiplicand) * multiplier "jmp 1f\n" "0:\n\t" "mull %4\n\t" // p2 = hi(multiplicand) * multiplier "sub %4, %%edx\n" // hi(p2) += -1 * multiplier "1:\n\t" "xchg %%eax, %2\n\t" // Save lo(p2), get lo(multiplicand) "mov %%edx, %1\n\t" // Save hi(p2) "mull %4\n\t" // p1 = lo(multiplicand) * multiplier "addl %2, %%edx\n\t" // hi(p1) += lo(p2) "adcl $0, %1\n\t" // hi(p2) += carry from previous step "cmpl $32, %%ecx\n\t" // shift < 32? "jl 2f\n\t" // Go if so "mov %%edx, %%eax\n\t" // result = hi(p2):hi(p1) >> (shift & 31) "mov %1, %%edx\n\t" "shrdl %%edx, %%eax\n\t" "sarl %%cl, %%edx\n\t" "jmp 3f\n" "2:\n\t" "shrdl %%edx, %%eax\n\t" // result = hi(p2):hi(p1):lo(p1) >> shift "shrdl %1, %%edx\n" "3:\n\t" : "=A" (result), "=&r" (tmp1), "=&r" (tmp2) : "0" (multiplicand), "rm" (multiplier), "c" (shift) : "cc"); return result; } #elif _MSC_VER #pragma warning(disable: 4035) static INLINE int64 Muls64x32s64(int64 multiplicand, uint32 multiplier, uint32 shift) { //ASSERT(shift >= 0 && shift < 64); /* Written and tested by mann, checked by dbudko and hpreg */ __asm { mov eax, DWORD PTR [multiplicand+4] // Get hi(multiplicand) test eax, eax // Check sign of multiplicand jl SHORT l0 // Go if negative mul DWORD PTR [multiplier] // p2 = hi(multiplicand) * multiplier jmp SHORT l1 l0: mul DWORD PTR [multiplier] // p2 = hi(multiplicand) * multiplier sub edx, DWORD PTR [multiplier] // hi(p2) += -1 * multiplier l1: mov ecx, eax // Save lo(p2) mov ebx, edx // Save hi(p2) mov eax, DWORD PTR [multiplicand] // Get lo(multiplicand) mul DWORD PTR [multiplier] // p1 = lo(multiplicand) * multiplier add edx, ecx // hi(p1) += lo(p2) adc ebx, 0 // hi(p2) += carry from previous step mov ecx, DWORD PTR [shift] // Get shift cmp ecx, 32 // shift < 32? jl SHORT l2 // Go if so mov eax, edx // result = hi(p2):hi(p1) >> (shift & 31) mov edx, ebx shrd eax, edx, cl sar edx, cl jmp SHORT l3 l2: shrd eax, edx, cl // result = hi(p2):hi(p1):lo(p1) << shift shrd edx, ebx, cl l3: } // return with result in edx:eax } #pragma warning(default: 4035) #else #error No compiler defined for Muls64x32s64 #endif #endif vmhgfs-only/vm_basic_asm_x86_64.h0000444000000000000000000002136012025726745015651 0ustar rootroot/********************************************************* * Copyright (C) 1998-2004 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 * *********************************************************/ /* * vm_basic_asm_x86_64.h * * Basic x86_64 asm macros. */ #ifndef _VM_BASIC_ASM_X86_64_H_ #define _VM_BASIC_ASM_X86_64_H_ #define INCLUDE_ALLOW_USERLEVEL #define INCLUDE_ALLOW_VMMEXT #define INCLUDE_ALLOW_MODULE #define INCLUDE_ALLOW_VMMON #define INCLUDE_ALLOW_VMK_MODULE #define INCLUDE_ALLOW_VMKERNEL #define INCLUDE_ALLOW_DISTRIBUTE #define INCLUDE_ALLOW_VMCORE #define INCLUDE_ALLOW_VMNIXMOD #include "includeCheck.h" #ifndef VM_X86_64 #error "This file is x86-64 only!" #endif #ifdef _MSC_VER #ifdef __cplusplus extern "C" { #endif uint64 _umul128(uint64 multiplier, uint64 multiplicand, uint64 *highProduct); int64 _mul128(int64 multiplier, int64 multiplicand, int64 *highProduct); uint64 __shiftright128(uint64 lowPart, uint64 highPart, uint8 shift); #ifdef __cplusplus } #endif #pragma intrinsic(_umul128, _mul128, __shiftright128) #endif // _MSC_VER /* * FXSAVE/FXRSTOR * save/restore SIMD/MMX fpu state * * The pointer passed in must be 16-byte aligned. * * Intel and AMD processors behave differently w.r.t. fxsave/fxrstor. Intel * processors unconditionally save the exception pointer state (instruction * ptr., data ptr., and error instruction opcode). FXSAVE_ES1 and FXRSTOR_ES1 * work correctly for Intel processors. * * AMD processors only save the exception pointer state if ES=1. This leads to a * security hole whereby one process/VM can inspect the state of another process * VM. The AMD recommended workaround involves clobbering the exception pointer * state unconditionally, and this is implemented in FXRSTOR_AMD_ES0. Note that * FXSAVE_ES1 will only save the exception pointer state for AMD processors if * ES=1. * * The workaround (FXRSTOR_AMD_ES0) only costs 1 cycle more than just doing an * fxrstor, on both AMD Opteron and Intel Core CPUs. */ #if defined(__GNUC__) static INLINE void FXSAVE_ES1(uint8 *save) { __asm__ __volatile__ ("fxsaveq %0 \n" : "=m" (*save) : : "memory"); } static INLINE void FXSAVE_COMPAT_ES1(uint8 *save) { __asm__ __volatile__ ("fxsave %0 \n" : "=m" (*save) : : "memory"); } static INLINE void FXRSTOR_ES1(const uint8 *load) { __asm__ __volatile__ ("fxrstorq %0 \n" : : "m" (*load) : "memory"); } static INLINE void FXRSTOR_COMPAT_ES1(const uint8 *load) { __asm__ __volatile__ ("fxrstor %0 \n" : : "m" (*load) : "memory"); } static INLINE void FXRSTOR_AMD_ES0(const uint8 *load) { uint64 dummy = 0; __asm__ __volatile__ ("fnstsw %%ax \n" // Grab x87 ES bit "bt $7,%%ax \n" // Test ES bit "jnc 1f \n" // Jump if ES=0 "fnclex \n" // ES=1. Clear it so fild doesn't trap "1: \n" "ffree %%st(7) \n" // Clear tag bit - avoid poss. stack overflow "fildl %0 \n" // Dummy Load from "safe address" changes all // x87 exception pointers. "fxrstorq %1 \n" : : "m" (dummy), "m" (*load) : "ax", "memory"); } #endif /* __GNUC__ */ /* *----------------------------------------------------------------------------- * * Mul64x3264 -- * * Unsigned integer by fixed point multiplication: * result = multiplicand * multiplier >> shift * * Unsigned 64-bit integer multiplicand. * Unsigned 32-bit fixed point multiplier, represented as * multiplier >> shift, where shift < 64. * Unsigned 64-bit integer product. * * Implementation: * Multiply 64x64 bits to yield a full 128-bit product. * Shift result in RDX:RAX right by "shift". * Return the low-order 64 bits of the above. * * Result: * Product * *----------------------------------------------------------------------------- */ #if defined(__GNUC__) static INLINE uint64 Mul64x3264(uint64 multiplicand, uint32 multiplier, uint32 shift) { uint64 result, dummy; const uint64 multiplier64 = multiplier; asm("mulq %3 \n\t" "shrdq %1, %0 \n\t" : "=a" (result), "=d" (dummy) : "0" (multiplier64), "rm" (multiplicand), "c" (shift) : "cc"); return result; } #elif defined(_MSC_VER) static INLINE uint64 Mul64x3264(uint64 multiplicand, uint32 multiplier, uint32 shift) { uint64 tmplo, tmphi; tmplo = _umul128(multiplicand, multiplier, &tmphi); return __shiftright128(tmplo, tmphi, (uint8) shift); } #endif /* *----------------------------------------------------------------------------- * * Muls64x32s64 -- * * Signed integer by fixed point multiplication: * result = multiplicand * multiplier >> shift * * Signed 64-bit integer multiplicand. * Unsigned 32-bit fixed point multiplier, represented as * multiplier >> shift, where shift < 64. * Signed 64-bit integer product. * * Implementation: * Multiply 64x64 bits to yield a full 128-bit product. * Shift result in RDX:RAX right by "shift". * Return the low-order 64 bits of the above. * * Note: using an unsigned shift instruction is correct because * shift < 64 and we return only the low 64 bits of the shifted * result. * * Result: * Product * *----------------------------------------------------------------------------- */ #if defined(__GNUC__) static inline int64 Muls64x32s64(int64 multiplicand, uint32 multiplier, uint32 shift) { int64 result, dummy; const int64 multiplier64 = multiplier; asm("imulq %3 \n\t" "shrdq %1, %0 \n\t" : "=a" (result), "=d" (dummy) : "0" (multiplier64), "rm" (multiplicand), "c" (shift) : "cc"); return result; } #elif defined(_MSC_VER) static INLINE int64 Muls64x32s64(int64 multiplicand, uint32 multiplier, uint32 shift) { int64 tmplo, tmphi; tmplo = _mul128(multiplicand, multiplier, &tmphi); return __shiftright128(tmplo, tmphi, (uint8) shift); } #endif #if defined(__GNUC__) static INLINE void * uint64set(void *dst, uint64 val, uint64 count) { int dummy0; int dummy1; __asm__ __volatile__("\t" "cld" "\n\t" "rep ; stosq" "\n" : "=c" (dummy0), "=D" (dummy1) : "0" (count), "1" (dst), "a" (val) : "memory", "cc"); return dst; } #endif /* *----------------------------------------------------------------------------- * * Div643232 -- * * Unsigned integer division: * The dividend is 64-bit wide * The divisor is 32-bit wide * The quotient is 32-bit wide * * Use this function if you are certain that the quotient will fit in 32 bits, * If that is not the case, a #DE exception was generated in 32-bit version, * but not in this 64-bit version. So please be careful. * * Results: * Quotient and remainder * * Side effects: * None * *----------------------------------------------------------------------------- */ #if defined(__GNUC__) || defined(_MSC_VER) static INLINE void Div643232(uint64 dividend, // IN uint32 divisor, // IN uint32 *quotient, // OUT uint32 *remainder) // OUT { *quotient = (uint32)(dividend / divisor); *remainder = (uint32)(dividend % divisor); } #endif /* *----------------------------------------------------------------------------- * * Div643264 -- * * Unsigned integer division: * The dividend is 64-bit wide * The divisor is 32-bit wide * The quotient is 64-bit wide * * Results: * Quotient and remainder * * Side effects: * None * *----------------------------------------------------------------------------- */ #if defined(__GNUC__) static INLINE void Div643264(uint64 dividend, // IN uint32 divisor, // IN uint64 *quotient, // OUT uint32 *remainder) // OUT { *quotient = dividend / divisor; *remainder = dividend % divisor; } #endif #endif // _VM_BASIC_ASM_X86_64_H_ vmhgfs-only/vm_basic_defs.h0000444000000000000000000003156512025726745015004 0ustar rootroot/********************************************************* * Copyright (C) 2003 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 * *********************************************************/ /* * vm_basic_defs.h -- * * Standard macros for VMware source code. */ #ifndef _VM_BASIC_DEFS_H_ #define _VM_BASIC_DEFS_H_ #define INCLUDE_ALLOW_USERLEVEL #define INCLUDE_ALLOW_VMMEXT #define INCLUDE_ALLOW_MODULE #define INCLUDE_ALLOW_VMMON #define INCLUDE_ALLOW_VMNIXMOD #define INCLUDE_ALLOW_VMKERNEL #define INCLUDE_ALLOW_VMKDRIVERS #define INCLUDE_ALLOW_VMK_MODULE #define INCLUDE_ALLOW_DISTRIBUTE #define INCLUDE_ALLOW_VMCORE #define INCLUDE_ALLOW_VMIROM #include "includeCheck.h" #include "vm_basic_types.h" // For INLINE. #if defined _WIN32 && defined USERLEVEL #include /* * We re-define offsetof macro from stddef, make * sure that its already defined before we do it */ #include // for Sleep() and LOWORD() etc. #endif /* * Simple macros */ #if defined __APPLE__ && !defined KERNEL # include #else // XXX the __cplusplus one matches that of VC++, to prevent redefinition warning // XXX the other one matches that of gcc3.3.3/glibc2.2.4 to prevent redefinition warnings #ifndef offsetof #ifdef __cplusplus #define offsetof(s,m) (size_t)&(((s *)0)->m) #else #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #endif #endif #endif // __APPLE__ #ifndef ARRAYSIZE #define ARRAYSIZE(a) (sizeof (a) / sizeof *(a)) #endif #ifndef MIN #define MIN(_a, _b) (((_a) < (_b)) ? (_a) : (_b)) #endif /* The Solaris 9 cross-compiler complains about these not being used */ #ifndef sun static INLINE int Min(int a, int b) { return a < b ? a : b; } #endif #ifndef MAX #define MAX(_a, _b) (((_a) > (_b)) ? (_a) : (_b)) #endif #ifndef sun static INLINE int Max(int a, int b) { return a > b ? a : b; } #endif #define ROUNDUP(x,y) (((x) + (y) - 1) / (y) * (y)) #define ROUNDDOWN(x,y) ((x) / (y) * (y)) #define ROUNDUPBITS(x, bits) (((uintptr_t) (x) + MASK(bits)) & ~MASK(bits)) #define ROUNDDOWNBITS(x, bits) ((uintptr_t) (x) & ~MASK(bits)) #define CEILING(x, y) (((x) + (y) - 1) / (y)) #if defined __APPLE__ #include #undef MASK #endif #define MASK(n) ((1 << (n)) - 1) /* make an n-bit mask */ #define DWORD_ALIGN(x) ((((x)+3) >> 2) << 2) #define QWORD_ALIGN(x) ((((x)+4) >> 3) << 3) #define IMPLIES(a,b) (!(a) || (b)) /* * Not everybody (e.g., the monitor) has NULL */ #ifndef NULL #ifdef __cplusplus #define NULL 0 #else #define NULL ((void *)0) #endif #endif /* * Token concatenation * * The C preprocessor doesn't prescan arguments when they are * concatenated or stringified. So we need extra levels of * indirection to convince the preprocessor to expand its * arguments. */ #define CONC(x, y) x##y #define XCONC(x, y) CONC(x, y) #define XXCONC(x, y) XCONC(x, y) #define MAKESTR(x) #x #define XSTR(x) MAKESTR(x) /* * Page operations * * It has been suggested that these definitions belong elsewhere * (like x86types.h). However, I deem them common enough * (since even regular user-level programs may want to do * page-based memory manipulation) to be here. * -- edward */ #ifndef PAGE_SHIFT // { #if defined VM_I386 #define PAGE_SHIFT 12 #elif defined __APPLE__ #define PAGE_SHIFT 12 #else #error #endif #endif // } #ifndef PAGE_SIZE #define PAGE_SIZE (1<> PAGE_SHIFT) #endif #ifndef BYTES_2_PAGES #define BYTES_2_PAGES(_nbytes) ((_nbytes) >> PAGE_SHIFT) #endif #ifndef PAGES_2_BYTES #define PAGES_2_BYTES(_npages) (((uint64)(_npages)) << PAGE_SHIFT) #endif #ifndef MBYTES_2_PAGES #define MBYTES_2_PAGES(_nbytes) ((_nbytes) << (20 - PAGE_SHIFT)) #endif #ifndef PAGES_2_MBYTES #define PAGES_2_MBYTES(_npages) ((_npages) >> (20 - PAGE_SHIFT)) #endif #ifndef VM_PAE_LARGE_PAGE_SHIFT #define VM_PAE_LARGE_PAGE_SHIFT 21 #endif #ifndef VM_PAE_LARGE_PAGE_SIZE #define VM_PAE_LARGE_PAGE_SIZE (1 << VM_PAE_LARGE_PAGE_SHIFT) #endif #ifndef VM_PAE_LARGE_PAGE_MASK #define VM_PAE_LARGE_PAGE_MASK (VM_PAE_LARGE_PAGE_SIZE - 1) #endif #ifndef VM_PAE_LARGE_2_SMALL_PAGES #define VM_PAE_LARGE_2_SMALL_PAGES (BYTES_2_PAGES(VM_PAE_LARGE_PAGE_SIZE)) #endif /* * Word operations */ #ifndef LOWORD #define LOWORD(_dw) ((_dw) & 0xffff) #endif #ifndef HIWORD #define HIWORD(_dw) (((_dw) >> 16) & 0xffff) #endif #ifndef LOBYTE #define LOBYTE(_w) ((_w) & 0xff) #endif #ifndef HIBYTE #define HIBYTE(_w) (((_w) >> 8) & 0xff) #endif #define HIDWORD(_qw) ((uint32)((_qw) >> 32)) #define LODWORD(_qw) ((uint32)(_qw)) #define QWORD(_hi, _lo) ((((uint64)(_hi)) << 32) | ((uint32)(_lo))) /* * Deposit a field _src at _pos bits from the right, * with a length of _len, into the integer _target. */ #define DEPOSIT_BITS(_src,_pos,_len,_target) { \ unsigned mask = ((1 << _len) - 1); \ unsigned shiftedmask = ((1 << _len) - 1) << _pos; \ _target = (_target & ~shiftedmask) | ((_src & mask) << _pos); \ } /* * Get return address. */ #ifdef _MSC_VER #ifdef __cplusplus extern "C" #endif void *_ReturnAddress(void); #pragma intrinsic(_ReturnAddress) #define GetReturnAddress() _ReturnAddress() #elif __GNUC__ #define GetReturnAddress() __builtin_return_address(0) #endif #ifdef __GNUC__ #ifndef sun /* * Get the frame pointer. We use this assembly hack instead of * __builtin_frame_address() due to a bug introduced in gcc 4.1.1 */ static INLINE_SINGLE_CALLER uintptr_t GetFrameAddr(void) { uintptr_t bp; #if (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ == 0)) bp = (uintptr_t)__builtin_frame_address(0); #elif (__GNUC__ == 4 && __GNUC_MINOR__ == 1 && __GNUC_PATCHLEVEL__ <= 3) # if defined(VMM64) || defined(VM_X86_64) __asm__ __volatile__("movq %%rbp, %0\n" : "=g" (bp)); # else __asm__ __volatile__("movl %%ebp, %0\n" : "=g" (bp)); # endif #else __asm__ __volatile__( #ifdef __linux__ ".print \"This newer version of GCC may or may not have the " "__builtin_frame_address bug. Need to update this. " "See bug 147638.\"\n" ".abort" #else /* MacOS */ ".abort \"This newer version of GCC may or may not have the " "__builtin_frame_address bug. Need to update this. " "See bug 147638.\"\n" #endif : "=g" (bp) ); #endif return bp; } /* * Returns the frame pointer of the calling function. * Equivalent to __builtin_frame_address(1). */ static INLINE_SINGLE_CALLER uintptr_t GetCallerFrameAddr(void) { return *(uintptr_t*)GetFrameAddr(); } #endif // sun #endif // __GNUC__ /* * Data prefetch was added in gcc 3.1.1 * http://www.gnu.org/software/gcc/gcc-3.1/changes.html */ #ifdef __GNUC__ # if ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ > 1) || \ (__GNUC__ == 3 && __GNUC_MINOR__ == 1 && __GNUC_PATCHLEVEL__ >= 1)) # define PREFETCH_R(var) __builtin_prefetch((var), 0 /* read */, \ 3 /* high temporal locality */) # define PREFETCH_W(var) __builtin_prefetch((var), 1 /* write */, \ 3 /* high temporal locality */) # else # define PREFETCH_R(var) ((void)(var)) # define PREFETCH_W(var) ((void)(var)) # endif #endif /* __GNUC__ */ #ifdef USERLEVEL // { /* * Note this might be a problem on NT b/c while sched_yield guarantees it * moves you to the end of your priority list, Sleep(0) offers no such * guarantee. Bummer. --Jeremy. */ #if defined(N_PLAT_NLM) || defined(__FreeBSD__) /* We do not have YIELD() as we do not need it yet... */ #elif defined(_WIN32) # define YIELD() Sleep(0) #else # include // For sched_yield. Don't ask. --Jeremy. # define YIELD() sched_yield() #endif /* * Standardize some Posix names on Windows. */ #ifdef _WIN32 // { #define snprintf _snprintf #define vsnprintf _vsnprintf #define strtok_r strtok_s static INLINE void sleep(unsigned int sec) { Sleep(sec * 1000); } static INLINE void usleep(unsigned long usec) { Sleep(CEILING(usec, 1000)); } typedef int pid_t; #define F_OK 0 #define X_OK 1 #define W_OK 2 #define R_OK 4 #endif // } /* * Macro for username comparison. */ #ifdef _WIN32 // { #define USERCMP(x,y) Str_Strcasecmp(x,y) #else #define USERCMP(x,y) strcmp(x,y) #endif // } #endif // } #ifndef va_copy #ifdef _WIN32 /* * Windows needs va_copy. This works for both 32 and 64-bit Windows * based on inspection of how varags.h from the Visual C CRTL is * implemented. (Future versions of the RTL may break this). */ #define va_copy(dest, src) ((dest) = (src)) #elif defined(__APPLE__) && defined(KERNEL) /* * MacOS kernel-mode needs va_copy. Based on inspection of stdarg.h * from the MacOSX10.4u.sdk kernel framework, this should work. * (Future versions of the SDK may break this). */ #define va_copy(dest, src) ((dest) = (src)) #elif defined(__GNUC__) && (__GNUC__ < 3) /* * Old versions of gcc recognize __va_copy, but not va_copy. */ #define va_copy(dest, src) __va_copy(dest, src) #endif // _WIN32 #endif // va_copy /* * This one is outside USERLEVEL because it's used by * files compiled into the Windows hgfs driver or the display * driver. */ #ifdef _WIN32 #define PATH_MAX 256 #ifndef strcasecmp #define strcasecmp(_s1,_s2) _stricmp((_s1),(_s2)) #endif #ifndef strncasecmp #define strncasecmp(_s1,_s2,_n) _strnicmp((_s1),(_s2),(_n)) #endif #endif /* * Convenience macro for COMMUNITY_SOURCE */ #undef EXCLUDE_COMMUNITY_SOURCE #ifdef COMMUNITY_SOURCE #define EXCLUDE_COMMUNITY_SOURCE(x) #else #define EXCLUDE_COMMUNITY_SOURCE(x) x #endif #undef COMMUNITY_SOURCE_INTEL_SECRET #if !defined(COMMUNITY_SOURCE) || defined(INTEL_SOURCE) /* * It's ok to include INTEL_SECRET source code for non-commsrc, * or for drops directed at Intel. */ #define COMMUNITY_SOURCE_INTEL_SECRET #endif /* * Convenience macros and definitions. Can often be used instead of #ifdef. */ #undef DEBUG_ONLY #undef SL_DEBUG_ONLY #undef VMX86_SL_DEBUG #ifdef VMX86_DEBUG #define vmx86_debug 1 #define DEBUG_ONLY(x) x /* * Be very, very, very careful with SL_DEBUG. Pls ask ganesh or min before * using it. */ #define VMX86_SL_DEBUG #define vmx86_sl_debug 1 #define SL_DEBUG_ONLY(x) x #else #define vmx86_debug 0 #define DEBUG_ONLY(x) #define vmx86_sl_debug 0 #define SL_DEBUG_ONLY(x) #endif #ifdef VMX86_STATS #define vmx86_stats 1 #define STATS_ONLY(x) x #else #define vmx86_stats 0 #define STATS_ONLY(x) #endif #ifdef VMX86_DEVEL #define vmx86_devel 1 #define DEVEL_ONLY(x) x #else #define vmx86_devel 0 #define DEVEL_ONLY(x) #endif #ifdef VMX86_LOG #define vmx86_log 1 #define LOG_ONLY(x) x #else #define vmx86_log 0 #define LOG_ONLY(x) #endif #ifdef VMX86_VMM_SERIAL_LOGGING #define vmx86_vmm_serial_log 1 #define VMM_SERIAL_LOG_ONLY(x) x #else #define vmx86_vmm_serial_log 0 #define VMM_SERIAL_LOG_ONLY(x) #endif #ifdef VMX86_SERVER #define vmx86_server 1 #define SERVER_ONLY(x) x #define HOSTED_ONLY(x) #else #define vmx86_server 0 #define SERVER_ONLY(x) #define HOSTED_ONLY(x) x #endif #ifdef VMX86_WGS #define vmx86_wgs 1 #define WGS_ONLY(x) x #else #define vmx86_wgs 0 #define WGS_ONLY(x) #endif #ifdef VMKERNEL #define vmkernel 1 #define VMKERNEL_ONLY(x) x #else #define vmkernel 0 #define VMKERNEL_ONLY(x) #endif #ifdef _WIN32 #define WIN32_ONLY(x) x #define POSIX_ONLY(x) #else #define WIN32_ONLY(x) #define POSIX_ONLY(x) x #endif #ifdef VMM #define VMM_ONLY(x) x #define USER_ONLY(x) #else #define VMM_ONLY(x) #define USER_ONLY(x) x #endif /* VMVISOR ifdef only allowed in the vmkernel */ #ifdef VMKERNEL #ifdef VMVISOR #define vmvisor 1 #define VMVISOR_ONLY(x) x #else #define vmvisor 0 #define VMVISOR_ONLY(x) #endif #endif #ifdef _WIN32 #define VMW_INVALID_HANDLE INVALID_HANDLE_VALUE #else #define VMW_INVALID_HANDLE -1 #endif #endif // ifndef _VM_BASIC_DEFS_H_ vmhgfs-only/x86cpuid.h0000444000000000000000000012356212025726745013671 0ustar rootroot/********************************************************* * Copyright (C) 1998-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 * *********************************************************/ #ifndef _X86CPUID_H_ #define _X86CPUID_H_ /* http://www.sandpile.org/ia32/cpuid.htm */ #define INCLUDE_ALLOW_USERLEVEL #define INCLUDE_ALLOW_VMX #define INCLUDE_ALLOW_VMMEXT #define INCLUDE_ALLOW_VMKERNEL #define INCLUDE_ALLOW_MODULE #define INCLUDE_ALLOW_VMNIXMOD #define INCLUDE_ALLOW_DISTRIBUTE #define INCLUDE_ALLOW_VMK_MODULE #define INCLUDE_ALLOW_VMCORE #define INCLUDE_ALLOW_VMMON #include "includeCheck.h" #include "vm_basic_types.h" /* * The linux kernel's ptrace.h stupidly defines the bare * EAX/EBX/ECX/EDX, which wrecks havoc with our preprocessor tricks. */ #undef EAX #undef EBX #undef ECX #undef EDX typedef struct CPUIDRegs { uint32 eax, ebx, ecx, edx; } CPUIDRegs; typedef union CPUIDRegsUnion { uint32 array[4]; CPUIDRegs regs; } CPUIDRegsUnion; /* * Results of calling cpuid(eax, ecx) on all host logical CPU. */ #ifdef _MSC_VER #pragma warning (disable :4200) // non-std extension: zero-sized array in struct #endif typedef #include "vmware_pack_begin.h" struct CPUIDReply { /* * Unique host logical CPU identifier. It does not change across queries, so * we use it to correlate the replies of multiple queries. */ uint64 tag; // OUT CPUIDRegs regs; // OUT } #include "vmware_pack_end.h" CPUIDReply; typedef #include "vmware_pack_begin.h" struct CPUIDQuery { uint32 eax; // IN uint32 ecx; // IN uint32 numLogicalCPUs; // IN/OUT CPUIDReply logicalCPUs[0]; // OUT } #include "vmware_pack_end.h" CPUIDQuery; /* * CPUID levels the monitor caches and ones that are not cached, but * have fields defined below (short name and actual value). * * The first parameter defines whether the level is masked/tested * during power-on/migration. Any level which is marked as FALSE here * *must* have all field masks defined as IGNORE in CPUID_FIELD_DATA. * A static assert in lib/cpuidcompat/cpuidcompat.c will check this. * * IMPORTANT: WHEN ADDING A NEW FIELD TO THE CACHED LEVELS, make sure * you update vmcore/vmm/cpu/priv.c:Priv_CPUID() and vmcore/vmm64/bt/ * cpuid_shared.S (and geninfo) to include the new level. */ #define CPUID_CACHED_LEVELS \ CPUIDLEVEL(TRUE, 0, 0) \ CPUIDLEVEL(TRUE, 1, 1) \ CPUIDLEVEL(FALSE,400, 0x40000000) \ CPUIDLEVEL(FALSE,410, 0x40000010) \ CPUIDLEVEL(FALSE, 80, 0x80000000) \ CPUIDLEVEL(TRUE, 81, 0x80000001) \ CPUIDLEVEL(FALSE, 88, 0x80000008) \ CPUIDLEVEL(TRUE, 8A, 0x8000000A) #define CPUID_UNCACHED_LEVELS \ CPUIDLEVEL(FALSE, 4, 4) \ CPUIDLEVEL(FALSE, 5, 5) \ CPUIDLEVEL(FALSE, 6, 6) \ CPUIDLEVEL(FALSE, A, 0xA) \ CPUIDLEVEL(FALSE, 86, 0x80000006) \ CPUIDLEVEL(FALSE, 87, 0x80000007) \ #define CPUID_ALL_LEVELS \ CPUID_CACHED_LEVELS \ CPUID_UNCACHED_LEVELS /* Define cached CPUID levels in the form: CPUID_LEVEL_ */ typedef enum { #define CPUIDLEVEL(t, s, v) CPUID_LEVEL_##s, CPUID_CACHED_LEVELS #undef CPUIDLEVEL CPUID_NUM_LEVELS } CpuidLevels; /* * CPUID result registers */ #define CPUID_REGS \ CPUIDREG(EAX, eax) \ CPUIDREG(EBX, ebx) \ CPUIDREG(ECX, ecx) \ CPUIDREG(EDX, edx) typedef enum { #define CPUIDREG(uc, lc) CPUID_REG_##uc, CPUID_REGS #undef CPUIDREG CPUID_NUM_REGS } CpuidRegs; /* * CPU vendors */ typedef enum { CPUID_VENDOR_UNKNOWN, CPUID_VENDOR_COMMON, CPUID_VENDOR_INTEL, CPUID_VENDOR_AMD, CPUID_VENDOR_CYRIX, CPUID_NUM_VENDORS } CpuidVendors; #define CPUID_INTEL_VENDOR_STRING "GenuntelineI" #define CPUID_AMD_VENDOR_STRING "AuthcAMDenti" #define CPUID_CYRIX_VENDOR_STRING "CyriteadxIns" #define CPUID_HYPERV_HYPERVISOR_VENDOR_STRING "Microsoft Hv" #define CPUID_INTEL_VENDOR_STRING_FIXED "GenuineIntel" #define CPUID_AMD_VENDOR_STRING_FIXED "AuthenticAMD" #define CPUID_CYRIX_VENDOR_STRING_FIXED "CyrixInstead" /* * FIELDDEF can be defined to process the CPUID information provided * in the following CPUID_FIELD_DATA macro. The first parameter is * the CPUID level of the feature (must be defined in CPUID_*_LEVELS. * The second parameter is the register the field is contained in * (defined in CPUID_REGS). The third field is the vendor this * feature applies to. "COMMON" means all vendors apply. UNKNOWN may * not be used here. The fourth and fifth parameters are the bit * position of the field and the width, respectively. The sixth is * the text name of the field. * * The seventh and eighth parameters specify the default CPUID * behavior for power-on, guest view, and migration tests (cpt/rsm & * vmotion). The eighth parameter is ignored for types other than * MASK & TEST, and must be zero in this case. * * When adding a new field, be sure to consider its purpose. The * following list of types is provided in order of likely use. * * NOTE: this form of representation is separate from the masking * system specified via the config file. That is because this * representation must take into account multi-bit fields. * * HOST - Passthrough host value and cannot change during migration. * MASK, 0 - Hide from the guest, because we don't support it or we * don't want the guest to know that it exists. * IGNORE - Ignore this field for all tests * * (Think twice before using the below mask types/combinations) * * MASK, x - Force the guest to always see x, and don't compare for * migration -- only APIC as of today; it is controlled by * software and we know how to toggle it * TEST, x - Require host CPUID field to be x for power-on * RSVD - Hidden from the guest, but compared during migration * * * Table to explain mask type meanings: * * IGNR MASK TEST HOST RSVD * -------------------------------------------------------- * Req'd val for power-on - - x - - * Value guest sees * x * * 0 * Checked on migration? N N Y Y Y * * * - initial host's power-on CPUID value * * FIELDDEFA takes a ninth parameter, the name used when creating * accessor functions in lib/public/cpuidInfoFuncs.h. * * FLAGDEF/FLAGDEFA is defined identically to fields, but their * accessors are more appropriate for 1-bit flags. */ typedef enum { CPUID_FIELD_MASK_IGNORE, CPUID_FIELD_MASK_MASK, CPUID_FIELD_MASK_TEST, CPUID_FIELD_MASK_HOST, CPUID_FIELD_MASK_RSVD, CPUID_NUM_FIELD_MASKS } CpuidFieldMasks; typedef enum { CPUID_FIELD_SUPPORTED_NO, CPUID_FIELD_SUPPORTED_YES, CPUID_FIELD_SUPPORTED_ANY, CPUID_FIELD_SUPPORTED_NA, CPUID_NUM_FIELD_SUPPORTEDS } CpuidFieldSupported; /* LEVEL, REG, VENDOR, POS, SIZE, NAME, MON SUPP, MASK TYPE, SET TO, CPL3, [FUNC] */ #define CPUID_FIELD_DATA_LEVEL_0 \ FIELDDEF( 0, EAX, COMMON, 0, 32, NUMLEVELS, ANY, IGNORE, 0, FALSE) \ FIELDDEF( 0, EBX, COMMON, 0, 32, VENDOR1, YES, HOST, 0, TRUE) \ FIELDDEF( 0, ECX, COMMON, 0, 32, VENDOR3, YES, HOST, 0, TRUE) \ FIELDDEF( 0, EDX, COMMON, 0, 32, VENDOR2, YES, HOST, 0, TRUE) /* LEVEL, REG, VENDOR, POS, SIZE, NAME, MON SUPP, MASK TYPE, SET TO, CPL3, [FUNC] */ #define CPUID_FIELD_DATA_LEVEL_1 \ FIELDDEFA( 1, EAX, COMMON, 0, 4, STEPPING, ANY, IGNORE, 0, FALSE, STEPPING) \ FIELDDEFA( 1, EAX, COMMON, 4, 4, MODEL, ANY, IGNORE, 0, FALSE, MODEL) \ FIELDDEFA( 1, EAX, COMMON, 8, 4, FAMILY, YES, HOST, 0, FALSE, FAMILY) \ FIELDDEF( 1, EAX, COMMON, 12, 2, TYPE, ANY, IGNORE, 0, FALSE) \ FIELDDEFA( 1, EAX, COMMON, 16, 4, EXTMODEL, ANY, IGNORE, 0, FALSE, EXT_MODEL) \ FIELDDEFA( 1, EAX, COMMON, 20, 8, EXTFAMILY, YES, HOST, 0, FALSE, EXT_FAMILY) \ FIELDDEF( 1, EBX, COMMON, 0, 8, BRAND_ID, ANY, IGNORE, 0, FALSE) \ FIELDDEF( 1, EBX, COMMON, 8, 8, CLFL_SIZE, ANY, IGNORE, 0, FALSE) \ FIELDDEFA( 1, EBX, COMMON, 16, 8, LCPU_COUNT, ANY, IGNORE, 0, FALSE, LCPU_COUNT) \ FIELDDEFA( 1, EBX, COMMON, 24, 8, APICID, ANY, IGNORE, 0, FALSE, APICID) \ FLAGDEFA( 1, ECX, COMMON, 0, 1, SSE3, YES, HOST, 0, TRUE, SSE3) \ FLAGDEF( 1, ECX, INTEL, 2, 1, NDA2, NO, MASK, 0, FALSE) \ FLAGDEFA( 1, ECX, COMMON, 3, 1, MWAIT, NO, MASK, 0, FALSE, MWAIT) \ FLAGDEFA( 1, ECX, INTEL, 4, 1, DSCPL, NO, MASK, 0, FALSE, DSCPL) \ FLAGDEFA( 1, ECX, INTEL, 5, 1, VMX, NO, MASK, 0, FALSE, VMX) \ FLAGDEF( 1, ECX, INTEL, 6, 1, SMX, NO, MASK, 0, FALSE) \ FLAGDEF( 1, ECX, INTEL, 7, 1, EST, NO, MASK, 0, FALSE) \ FLAGDEF( 1, ECX, INTEL, 8, 1, TM2, NO, MASK, 0, FALSE) \ FLAGDEFA( 1, ECX, COMMON, 9, 1, SSSE3, YES, HOST, 0, TRUE, SSSE3) \ FLAGDEF( 1, ECX, INTEL, 10, 1, HTCACHE, NO, MASK, 0, FALSE) \ FLAGDEFA( 1, ECX, COMMON, 13, 1, CMPX16, YES, HOST, 0, TRUE, CMPX16) \ FLAGDEF( 1, ECX, INTEL, 14, 1, xPPR, NO, MASK, 0, FALSE) \ FLAGDEF( 1, ECX, INTEL, 15, 1, PERF_MSR, NO, MASK, 0, FALSE) \ FLAGDEF( 1, ECX, INTEL, 18, 1, DCA, NO, MASK, 0, FALSE) \ FLAGDEFA( 1, ECX, INTEL, 19, 1, SSE41, YES, HOST, 0, TRUE, SSE41) \ FLAGDEFA( 1, ECX, INTEL, 20, 1, SSE42, YES, HOST, 0, TRUE, SSE42) \ FLAGDEF( 1, ECX, INTEL, 21, 1, X2APIC, NO, MASK, 0, FALSE) \ FLAGDEF( 1, ECX, INTEL, 22, 1, MOVBE, NO, RSVD, 0, TRUE) \ FLAGDEFA( 1, ECX, COMMON, 23, 1, POPCNT, YES, HOST, 0, TRUE, POPCNT) \ FLAGDEF( 1, ECX, INTEL, 24, 1, ULE, NO, RSVD, 0, TRUE) \ FLAGDEF( 1, ECX, INTEL, 26, 1, XSAVE, NO, MASK, 0, FALSE) \ FLAGDEF( 1, ECX, INTEL, 27, 1, OSXSAVE, NO, RSVD, 0, TRUE) \ FLAGDEFA( 1, ECX, COMMON, 31, 1, HYPERVISOR, ANY, IGNORE, 0, FALSE, HYPERVISOR)\ FLAGDEFA( 1, EDX, COMMON, 0, 1, FPU, YES, HOST, 0, TRUE, FPU) \ FLAGDEFA( 1, EDX, COMMON, 1, 1, VME, YES, HOST, 0, FALSE, VME) \ FLAGDEF( 1, EDX, COMMON, 2, 1, DBGE, YES, HOST, 0, FALSE) \ FLAGDEF( 1, EDX, COMMON, 3, 1, PGSZE, YES, HOST, 0, FALSE) \ FLAGDEFA( 1, EDX, COMMON, 4, 1, TSC, YES, HOST, 0, TRUE, TSC) \ FLAGDEF( 1, EDX, COMMON, 5, 1, MSR, YES, HOST, 0, FALSE) \ FLAGDEFA( 1, EDX, COMMON, 6, 1, PAE, YES, HOST, 0, FALSE, PAE) \ FLAGDEF( 1, EDX, COMMON, 7, 1, MCK, YES, HOST, 0, FALSE) \ FLAGDEF( 1, EDX, COMMON, 8, 1, CPMX, YES, HOST, 0, TRUE) \ FLAGDEFA( 1, EDX, COMMON, 9, 1, APIC, ANY, MASK, 1, FALSE, APIC) \ FLAGDEFA( 1, EDX, COMMON, 11, 1, SEP, YES, HOST, 0, TRUE, SEP) \ FLAGDEFA( 1, EDX, COMMON, 12, 1, MTRR, YES, HOST, 0, FALSE, MTRR) \ FLAGDEFA( 1, EDX, COMMON, 13, 1, PGE, YES, HOST, 0, FALSE, PGE) \ FLAGDEFA( 1, EDX, COMMON, 14, 1, MCA, YES, HOST, 0, FALSE, MCA) \ FLAGDEFA( 1, EDX, COMMON, 15, 1, CMOV, YES, HOST, 0, TRUE, CMOV) \ FLAGDEFA( 1, EDX, COMMON, 16, 1, PAT, YES, HOST, 0, FALSE, PAT) \ FLAGDEF( 1, EDX, COMMON, 17, 1, 36PG, YES, HOST, 0, FALSE) \ FLAGDEF( 1, EDX, INTEL, 18, 1, PSN, YES, HOST, 0, FALSE) \ FLAGDEFA( 1, EDX, COMMON, 19, 1, CLFL, YES, HOST, 0, TRUE, CLFL) \ FLAGDEF( 1, EDX, INTEL, 21, 1, DTES, YES, HOST, 0, FALSE) \ FLAGDEF( 1, EDX, INTEL, 22, 1, ACPI, YES, HOST, 0, FALSE) \ FLAGDEFA( 1, EDX, COMMON, 23, 1, MMX, YES, HOST, 0, TRUE, MMX) \ FLAGDEFA( 1, EDX, COMMON, 24, 1, FXSAVE, YES, HOST, 0, TRUE, FXSAVE) \ FLAGDEFA( 1, EDX, COMMON, 25, 1, SSE, YES, HOST, 0, TRUE, SSE) \ FLAGDEFA( 1, EDX, COMMON, 26, 1, SSE2, YES, HOST, 0, TRUE, SSE2) \ FLAGDEF( 1, EDX, INTEL, 27, 1, SS, YES, HOST, 0, FALSE) \ FLAGDEFA( 1, EDX, COMMON, 28, 1, HT, NO, MASK, 0, FALSE, HT) \ FLAGDEF( 1, EDX, INTEL, 29, 1, TM, NO, MASK, 0, FALSE) \ FLAGDEF( 1, EDX, INTEL, 30, 1, IA64, NO, MASK, 0, FALSE) \ FLAGDEF( 1, EDX, INTEL, 31, 1, PBE, NO, MASK, 0, FALSE) /* LEVEL, REG, VENDOR, POS, SIZE, NAME, MON SUPP, MASK TYPE, SET TO, CPL3, [FUNC] */ #define CPUID_FIELD_DATA_LEVEL_4 \ FIELDDEF( 4, EAX, INTEL, 0, 5, CACHE_TYPE, NA, IGNORE, 0, FALSE) \ FIELDDEF( 4, EAX, INTEL, 5, 3, CACHE_LEVEL, NA, IGNORE, 0, FALSE) \ FIELDDEF( 4, EAX, INTEL, 14, 12, CACHE_NUMHT_SHARING, NA, IGNORE, 0, FALSE) \ FIELDDEFA( 4, EAX, INTEL, 26, 6, CORE_COUNT, NA, IGNORE, 0, FALSE, INTEL_CORE_COUNT) \ FIELDDEF( 4, EBX, INTEL, 0, 12, CACHE_LINE, NA, IGNORE, 0, FALSE) \ FIELDDEF( 4, EBX, INTEL, 12, 10, CACHE_PART, NA, IGNORE, 0, FALSE) \ FIELDDEF( 4, EBX, INTEL, 22, 10, CACHE_WAYS, NA, IGNORE, 0, FALSE) /* LEVEL, REG, VENDOR, POS, SIZE, NAME, MON SUPP, MASK TYPE, SET TO, CPL3, [FUNC] */ #define CPUID_FIELD_DATA_LEVEL_5 \ FIELDDEF( 5, EAX, COMMON, 0, 16, MWAIT_MIN_SIZE, NA, IGNORE, 0, FALSE) \ FIELDDEF( 5, EBX, COMMON, 0, 16, MWAIT_MAX_SIZE, NA, IGNORE, 0, FALSE) \ FLAGDEF( 5, ECX, COMMON, 0, 1, MWAIT_EXTENSIONS, NA, IGNORE, 0, FALSE) \ FLAGDEF( 5, ECX, COMMON, 1, 1, MWAIT_INTR_BREAK, NA, IGNORE, 0, FALSE) \ FIELDDEF( 5, EDX, INTEL, 0, 4, MWAIT_C0_SUBSTATE, NA, IGNORE, 0, FALSE) \ FIELDDEF( 5, EDX, INTEL, 4, 4, MWAIT_C1_SUBSTATE, NA, IGNORE, 0, FALSE) \ FIELDDEF( 5, EDX, INTEL, 8, 4, MWAIT_C2_SUBSTATE, NA, IGNORE, 0, FALSE) \ FIELDDEF( 5, EDX, INTEL, 12, 4, MWAIT_C3_SUBSTATE, NA, IGNORE, 0, FALSE) \ FIELDDEF( 5, EDX, INTEL, 16, 4, MWAIT_C4_SUBSTATE, NA, IGNORE, 0, FALSE) /* LEVEL, REG, VENDOR, POS, SIZE, NAME, MON SUPP, MASK TYPE, SET TO, CPL3, [FUNC] */ #define CPUID_FIELD_DATA_LEVEL_6 \ FLAGDEF( 6, EAX, INTEL, 0, 1, THERMAL_SENSOR, NA, IGNORE, 0, FALSE) \ FLAGDEF( 6, EAX, INTEL, 1, 1, TURBO_MODE, NA, IGNORE, 0, FALSE) \ FIELDDEF( 6, EBX, INTEL, 0, 4, NUM_INTR_THRESHOLDS, NA, IGNORE, 0, FALSE) \ FLAGDEF( 6, ECX, INTEL, 0, 1, HW_COORD_FEEDBACK, NA, IGNORE, 0, FALSE) /* LEVEL, REG, VENDOR, POS, SIZE, NAME, MON SUPP, MASK TYPE, SET TO, CPL3, [FUNC] */ #define CPUID_FIELD_DATA_LEVEL_A \ FIELDDEFA( A, EAX, INTEL, 0, 8, PMC_VERSION, NA, IGNORE, 0, FALSE, PMC_VERSION) \ FIELDDEFA( A, EAX, INTEL, 8, 8, NUM_PMCS, NA, IGNORE, 0, FALSE, NUM_PMCS) \ FIELDDEF( A, EAX, INTEL, 16, 8, PMC_BIT_WIDTH, NA, IGNORE, 0, FALSE) \ FIELDDEFA( A, EAX, INTEL, 24, 8, PMC_EBX_LENGTH, NA, IGNORE, 0, FALSE, PMC_EBX_LENGTH) \ FLAGDEF( A, EBX, INTEL, 0, 1, PMC_CORE_CYCLE, NA, IGNORE, 0, FALSE) \ FLAGDEF( A, EBX, INTEL, 1, 1, PMC_INSTR_RETIRED, NA, IGNORE, 0, FALSE) \ FLAGDEF( A, EBX, INTEL, 2, 1, PMC_REF_CYCLES, NA, IGNORE, 0, FALSE) \ FLAGDEF( A, EBX, INTEL, 3, 1, PMC_LAST_LVL_CREF, NA, IGNORE, 0, FALSE) \ FLAGDEF( A, EBX, INTEL, 4, 1, PMC_LAST_LVL_CMISS, NA, IGNORE, 0, FALSE) \ FLAGDEF( A, EBX, INTEL, 5, 1, PMC_BR_INST_RETIRED, NA, IGNORE, 0, FALSE) \ FLAGDEF( A, EBX, INTEL, 6, 1, PMC_BR_MISS_RETIRED, NA, IGNORE, 0, FALSE) \ FIELDDEF( A, EDX, INTEL, 0, 5, PMC_FIXED_NUM, NA, IGNORE, 0, FALSE) \ FIELDDEF( A, EDX, INTEL, 5, 8, PMC_FIXED_SIZE, NA, IGNORE, 0, FALSE) /* LEVEL, REG, VENDOR, POS, SIZE, NAME, MON SUPP, MASK TYPE, SET TO, CPL3, [FUNC] */ #define CPUID_FIELD_DATA_LEVEL_80 \ FIELDDEF( 80, EAX, COMMON, 0, 32, NUM_EXT_LEVELS, NA, IGNORE, 0, FALSE) \ FIELDDEF( 80, EBX, AMD, 0, 32, AMD_VENDOR1, NA, IGNORE, 0, FALSE) \ FIELDDEF( 80, ECX, AMD, 0, 32, AMD_VENDOR3, NA, IGNORE, 0, FALSE) \ FIELDDEF( 80, EDX, AMD, 0, 32, AMD_VENDOR2, NA, IGNORE, 0, FALSE) /* LEVEL, REG, VENDOR, POS, SIZE, NAME, MON SUPP, MASK TYPE, SET TO, CPL3, [FUNC] */ #define CPUID_FIELD_DATA_LEVEL_81 \ FIELDDEF( 81, EAX, INTEL, 0, 32, UNKNOWN81EAX, ANY, IGNORE, 0, FALSE) \ FIELDDEF( 81, EAX, AMD, 0, 4, STEPPING, ANY, IGNORE, 0, FALSE) \ FIELDDEF( 81, EAX, AMD, 4, 4, MODEL, ANY, IGNORE, 0, FALSE) \ FIELDDEF( 81, EAX, AMD, 8, 4, FAMILY, ANY, IGNORE, 0, FALSE) \ FIELDDEF( 81, EAX, AMD, 12, 2, TYPE, ANY, IGNORE, 0, FALSE) \ FIELDDEF( 81, EAX, AMD, 16, 4, EXTMODEL, ANY, IGNORE, 0, FALSE) \ FIELDDEF( 81, EAX, AMD, 20, 8, EXTFAMILY, ANY, IGNORE, 0, FALSE) \ FIELDDEF( 81, EBX, INTEL, 0, 32, UNKNOWN81EBX, ANY, IGNORE, 0, FALSE) \ FIELDDEF( 81, EBX, AMD, 0, 16, BRAND_ID, ANY, IGNORE, 0, FALSE) \ FIELDDEF( 81, EBX, AMD, 16, 16, UNDEF, ANY, IGNORE, 0, FALSE) \ FLAGDEFA( 81, ECX, COMMON, 0, 1, LAHF, YES, HOST, 0, TRUE, LAHF64) \ FLAGDEFA( 81, ECX, AMD, 1, 1, CMPLEGACY, NO, MASK, 0, FALSE, CMPLEGACY) \ FLAGDEFA( 81, ECX, AMD, 2, 1, SVM, NO, MASK, 0, FALSE, SVM) \ FLAGDEFA( 81, ECX, AMD, 3, 1, EXTAPICSPC, YES, HOST, 0, FALSE, EXTAPICSPC) \ FLAGDEFA( 81, ECX, AMD, 4, 1, CR8AVAIL, NO, MASK, 0, FALSE, CR8AVAIL) \ FLAGDEFA( 81, ECX, AMD, 5, 1, ABM, YES, HOST, 0, TRUE, ABM) \ FLAGDEFA( 81, ECX, AMD, 6, 1, SSE4A, YES, HOST, 0, TRUE, SSE4A) \ FLAGDEF( 81, ECX, AMD, 7, 1, MISALIGNED_SSE, YES, HOST, 0, TRUE) \ FLAGDEFA( 81, ECX, AMD, 8, 1, 3DNPREFETCH, YES, HOST, 0, TRUE, 3DNPREFETCH) \ FLAGDEF( 81, ECX, AMD, 9, 1, OSVW, NO, MASK, 0, FALSE) \ FLAGDEF( 81, ECX, AMD, 10, 1, IBS, NO, MASK, 0, FALSE) \ FLAGDEF( 81, ECX, AMD, 11, 1, SSE5, NO, RSVD, 0, TRUE) \ FLAGDEF( 81, ECX, AMD, 12, 1, SKINIT, NO, MASK, 0, FALSE) \ FLAGDEF( 81, ECX, AMD, 13, 1, WATCHDOG, NO, MASK, 0, FALSE) \ FLAGDEF( 81, EDX, AMD, 0, 1, FPU, YES, HOST, 0, TRUE) \ FLAGDEF( 81, EDX, AMD, 1, 1, VME, YES, HOST, 0, FALSE) \ FLAGDEF( 81, EDX, AMD, 2, 1, DBGE, YES, HOST, 0, FALSE) \ FLAGDEF( 81, EDX, AMD, 3, 1, PGSZE, YES, HOST, 0, FALSE) \ FLAGDEF( 81, EDX, AMD, 4, 1, TSC, YES, HOST, 0, TRUE) \ FLAGDEF( 81, EDX, AMD, 5, 1, MSR, YES, HOST, 0, FALSE) \ FLAGDEF( 81, EDX, AMD, 6, 1, PAE, YES, HOST, 0, FALSE) \ FLAGDEF( 81, EDX, AMD, 7, 1, MCK, YES, HOST, 0, FALSE) \ FLAGDEF( 81, EDX, AMD, 8, 1, CPMX, YES, HOST, 0, TRUE) \ FLAGDEF( 81, EDX, AMD, 9, 1, APIC, ANY, MASK, 1, FALSE) \ FLAGDEFA( 81, EDX, COMMON, 11, 1, SYSC, ANY, IGNORE, 0, TRUE, SYSC) \ FLAGDEF( 81, EDX, AMD, 12, 1, MTRR, YES, HOST, 0, FALSE) \ FLAGDEF( 81, EDX, AMD, 13, 1, PGE, YES, HOST, 0, FALSE) \ FLAGDEF( 81, EDX, AMD, 14, 1, MCA, YES, HOST, 0, FALSE) \ FLAGDEF( 81, EDX, AMD, 15, 1, CMOV, YES, HOST, 0, TRUE) \ FLAGDEF( 81, EDX, AMD, 16, 1, PAT, YES, HOST, 0, FALSE) \ FLAGDEF( 81, EDX, AMD, 17, 1, 36PG, YES, HOST, 0, FALSE) \ FLAGDEFA( 81, EDX, COMMON, 20, 1, NX, YES, HOST, 0, FALSE, NX) \ FLAGDEFA( 81, EDX, AMD, 22, 1, MMXEXT, YES, HOST, 0, TRUE, MMXEXT) \ FLAGDEF( 81, EDX, AMD, 23, 1, MMX, YES, HOST, 0, TRUE) \ FLAGDEF( 81, EDX, AMD, 24, 1, FXSAVE, YES, HOST, 0, TRUE) \ FLAGDEFA( 81, EDX, AMD, 25, 1, FFXSR, YES, HOST, 0, FALSE, FFXSR) \ FLAGDEF( 81, EDX, AMD, 26, 1, PDPE1GB, NO, MASK, 0, FALSE) \ FLAGDEFA( 81, EDX, COMMON, 27, 1, RDTSCP, YES, HOST, 0, TRUE, RDTSCP) \ FLAGDEFA( 81, EDX, COMMON, 29, 1, LM, YES, TEST, 1, FALSE, LM) \ FLAGDEFA( 81, EDX, AMD, 30, 1, 3DNOWPLUS, YES, HOST, 0, TRUE, 3DNOWPLUS) \ FLAGDEFA( 81, EDX, AMD, 31, 1, 3DNOW, YES, HOST, 0, TRUE, 3DNOW) /* LEVEL, REG, VENDOR, POS, SIZE, NAME, MON SUPP, MASK TYPE, SET TO, CPL3, [FUNC] */ #define CPUID_FIELD_DATA_LEVEL_8x \ FIELDDEF( 86, ECX, AMD, 0, 8, L2CACHE_LINE, NA, IGNORE, 0, FALSE) \ FIELDDEF( 86, ECX, AMD, 8, 4, L2CACHE_LINE_PER_TAG, NA, IGNORE, 0, FALSE) \ FIELDDEF( 86, ECX, AMD, 12, 4, L2CACHE_WAYS, NA, IGNORE, 0, FALSE) \ FIELDDEF( 86, ECX, AMD, 16, 16, L2CACHE_SIZE, NA, IGNORE, 0, FALSE) \ FIELDDEF( 86, EDX, AMD, 0, 8, L3CACHE_LINE, NA, IGNORE, 0, FALSE) \ FIELDDEF( 86, EDX, AMD, 8, 4, L3CACHE_LINE_PER_TAG,NA, IGNORE, 0, FALSE) \ FIELDDEF( 86, EDX, AMD, 12, 4, L3CACHE_WAYS, NA, IGNORE, 0, FALSE) \ FIELDDEF( 86, EDX, AMD, 18, 14, L3CACHE_SIZE, NA, IGNORE, 0, FALSE) \ FLAGDEF( 87, EDX, AMD, 0, 1, TS, NA, IGNORE, 0, FALSE) \ FLAGDEF( 87, EDX, AMD, 1, 1, FID, NA, IGNORE, 0, FALSE) \ FLAGDEF( 87, EDX, AMD, 2, 1, VID, NA, IGNORE, 0, FALSE) \ FLAGDEF( 87, EDX, AMD, 3, 1, TTP, NA, IGNORE, 0, FALSE) \ FLAGDEF( 87, EDX, AMD, 4, 1, TM, NA, IGNORE, 0, FALSE) \ FLAGDEF( 87, EDX, AMD, 5, 1, STC, NA, IGNORE, 0, FALSE) \ FLAGDEF( 87, EDX, AMD, 6, 1, 100MHZSTEPS, NA, IGNORE, 0, FALSE) \ FLAGDEF( 87, EDX, AMD, 7, 1, HWPSTATE, NA, IGNORE, 0, FALSE) \ FLAGDEF( 87, EDX, AMD, 8, 1, TSC_INVARIANT, NA, IGNORE, 0, FALSE) \ FIELDDEFA(88, EAX, COMMON, 0, 8, PHYSBITS, NA, IGNORE, 0, FALSE, PHYS_BITS) \ FIELDDEFA(88, EAX, COMMON, 8, 8, VIRTBITS, NA, IGNORE, 0, FALSE, VIRT_BITS) \ FIELDDEFA(88, ECX, AMD, 0, 8, CORE_COUNT, NA, IGNORE, 0, FALSE, AMD_CORE_COUNT) \ FIELDDEFA(88, ECX, AMD, 12, 4, APICID_COREID_SIZE, NA, IGNORE, 0, FALSE, AMD_APICID_COREID_SIZE) \ FIELDDEFA(8A, EAX, AMD, 0, 8, SVM_REVISION, NO, MASK, 0, FALSE, SVM_REVISION) \ FLAGDEF( 8A, EAX, AMD, 8, 1, SVM_HYPERVISOR, NO, MASK, 0, FALSE) \ FIELDDEF( 8A, EAX, AMD, 9, 23, SVMEAX_RSVD, NO, MASK, 0, FALSE) \ FIELDDEF( 8A, EBX, AMD, 0, 32, SVM_N_ASIDS, NO, MASK, 0, FALSE) \ FIELDDEF( 8A, ECX, AMD, 0, 32, SVMECX_RSVD, NO, MASK, 0, FALSE) \ FLAGDEFA( 8A, EDX, AMD, 0, 1, SVM_NP, NO, MASK, 0, FALSE, NPT) \ FLAGDEF( 8A, EDX, AMD, 1, 1, SVM_LBR, NO, MASK, 0, FALSE) \ FLAGDEF( 8A, EDX, AMD, 2, 1, SVM_LOCK, NO, MASK, 0, FALSE) \ FLAGDEF( 8A, EDX, AMD, 3, 1, SVM_NRIP, NO, MASK, 0, FALSE) \ FIELDDEF( 8A, EDX, AMD, 4, 28, SVMEDX_RSVD, NO, MASK, 0, FALSE) #define CPUID_FIELD_DATA \ CPUID_FIELD_DATA_LEVEL_0 \ CPUID_FIELD_DATA_LEVEL_1 \ CPUID_FIELD_DATA_LEVEL_4 \ CPUID_FIELD_DATA_LEVEL_5 \ CPUID_FIELD_DATA_LEVEL_6 \ CPUID_FIELD_DATA_LEVEL_A \ CPUID_FIELD_DATA_LEVEL_80 \ CPUID_FIELD_DATA_LEVEL_81 \ CPUID_FIELD_DATA_LEVEL_8x /* * Define all field and flag values as an enum. The result is a full * set of values taken from the table above in the form: * * CPUID_FEATURE__ID_ == mask for feature * CPUID__ID__MASK == mask for field * CPUID__ID__SHIFT == offset of field * * e.g. - CPUID_FEATURE_COMMON_ID1EDX_FPU = 0x1 * - CPUID_COMMON_ID88EAX_VIRTBITS_MASK = 0xff00 * - CPUID_COMMON_ID88EAX_VIRTBITS_SHIFT = 8 * * Note: The FEATURE/MASK definitions must use some gymnastics to get * around a warning when shifting left by 32. */ #define VMW_BIT_MASK(shift) (((1 << (shift - 1)) << 1) - 1) #define FIELDDEF(lvl, reg, vend, bitpos, size, name, s, m, v, c3) \ CPUID_##vend##_ID##lvl##reg##_##name##_SHIFT = bitpos, \ CPUID_##vend##_ID##lvl##reg##_##name##_MASK = \ VMW_BIT_MASK(size) << bitpos, \ CPUID_FEATURE_##vend##_ID##lvl##reg##_##name = \ CPUID_##vend##_ID##lvl##reg##_##name##_MASK, #define FIELDDEFA(lvl, reg, vend, bitpos, size, name, s, m, v, c3, f) \ FIELDDEF(lvl, reg, vend, bitpos, size, name, s, m, v, c3) #define FLAGDEFA FIELDDEFA #define FLAGDEF FIELDDEF enum { /* Define data for every CPUID field we have */ CPUID_FIELD_DATA }; #undef VMW_BIT_MASK #undef FIELDDEF #undef FLAGDEF #undef FIELDDEFA #undef FLAGDEFA /* * Legal CPUID config file mask characters. For a description of the * cpuid masking system, please see: * * http://vmweb.vmware.com/~mts/cgi-bin/view.cgi/Apps/CpuMigrationChecks */ #define CPUID_MASK_HIDE_CHR '0' #define CPUID_MASK_HIDE_STR "0" #define CPUID_MASK_FORCE_CHR '1' #define CPUID_MASK_FORCE_STR "1" #define CPUID_MASK_PASS_CHR '-' #define CPUID_MASK_PASS_STR "-" #define CPUID_MASK_TRUE_CHR 'T' #define CPUID_MASK_TRUE_STR "T" #define CPUID_MASK_FALSE_CHR 'F' #define CPUID_MASK_FALSE_STR "F" #define CPUID_MASK_IGNORE_CHR 'X' #define CPUID_MASK_IGNORE_STR "X" #define CPUID_MASK_HOST_CHR 'H' #define CPUID_MASK_HOST_STR "H" #define CPUID_MASK_RSVD_CHR 'R' #define CPUID_MASK_RSVD_STR "R" #define CPUID_MASK_INSTALL_CHR 'I' #define CPUID_MASK_INSTALL_STR "I" /* * If a level is listed as not masked/tested in CPUID_LEVELS above, * use all "don't care" values for its mask. */ #define CPT_DFLT_UNDEFINED_MASK "XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX" /* * When LM is disabled, we overlay the following masks onto the * guest's default masks. Any level that is not defined below should * be treated as all "-"s */ #define CPT_ID1ECX_LM_DISABLED "----:----:----:----:--0-:----:----:----" #define CPT_ID81EDX_LM_DISABLED "--0-:----:----:----:----:----:----:----" #define CPT_ID81ECX_LM_DISABLED "----:----:----:----:----:----:----:---0" #define CPT_GET_LM_DISABLED_MASK(lvl, reg) \ ((lvl == 1 && reg == CPUID_REG_ECX) ? CPT_ID1ECX_LM_DISABLED : \ (lvl == 0x80000001 && reg == CPUID_REG_ECX) ? CPT_ID81ECX_LM_DISABLED : \ (lvl == 0x80000001 && reg == CPUID_REG_EDX) ? CPT_ID81EDX_LM_DISABLED : \ NULL) /* * Macro to define GET and SET functions for various common CPUID * fields. To create function for a new field, simply name it (CPUID_ * and CPUID_SET_ are automatically prepended), and list the field * name that it needs to use. */ #define FIELD_FUNC(name, field) \ static INLINE uint32 CPUID_##name(uint32 reg) \ { \ return (reg & field##_MASK) >> field##_SHIFT; \ } \ static INLINE void CPUID_SET_##name(uint32 *reg, uint32 val) \ { \ *reg = (*reg & ~field##_MASK) | (val << field##_SHIFT); \ } FIELD_FUNC(STEPPING, CPUID_COMMON_ID1EAX_STEPPING) FIELD_FUNC(MODEL, CPUID_COMMON_ID1EAX_MODEL) FIELD_FUNC(FAMILY, CPUID_COMMON_ID1EAX_FAMILY) FIELD_FUNC(TYPE, CPUID_COMMON_ID1EAX_TYPE) FIELD_FUNC(EXTENDED_MODEL, CPUID_COMMON_ID1EAX_EXTMODEL) FIELD_FUNC(EXTENDED_FAMILY, CPUID_COMMON_ID1EAX_EXTFAMILY) FIELD_FUNC(LCPU_COUNT, CPUID_COMMON_ID1EBX_LCPU_COUNT) FIELD_FUNC(APICID, CPUID_COMMON_ID1EBX_APICID) FIELD_FUNC(PA_BITS, CPUID_COMMON_ID88EAX_PHYSBITS) FIELD_FUNC(VIRT_BITS, CPUID_COMMON_ID88EAX_VIRTBITS) FIELD_FUNC(SVM_REVISION, CPUID_AMD_ID8AEAX_SVM_REVISION) FIELD_FUNC(SVM_N_ASIDS, CPUID_AMD_ID8AEBX_SVM_N_ASIDS) FIELD_FUNC(INTEL_CORE_COUNT, CPUID_INTEL_ID4EAX_CORE_COUNT) FIELD_FUNC(AMD_CORE_COUNT, CPUID_AMD_ID88ECX_CORE_COUNT) FIELD_FUNC(AMD_APICID_COREID_SIZE, CPUID_AMD_ID88ECX_APICID_COREID_SIZE) FIELD_FUNC(AMD_EXTAPICSPC, CPUID_AMD_ID81ECX_EXTAPICSPC) FIELD_FUNC(NUM_PMCS, CPUID_INTEL_IDAEAX_NUM_PMCS) FIELD_FUNC(MWAIT_MIN_SIZE, CPUID_COMMON_ID5EAX_MWAIT_MIN_SIZE) FIELD_FUNC(MWAIT_MAX_SIZE, CPUID_COMMON_ID5EBX_MWAIT_MAX_SIZE) FIELD_FUNC(MWAIT_C0_SUBSTATE, CPUID_INTEL_ID5EDX_MWAIT_C0_SUBSTATE) FIELD_FUNC(MWAIT_C1_SUBSTATE, CPUID_INTEL_ID5EDX_MWAIT_C1_SUBSTATE) FIELD_FUNC(MWAIT_C2_SUBSTATE, CPUID_INTEL_ID5EDX_MWAIT_C2_SUBSTATE) FIELD_FUNC(MWAIT_C3_SUBSTATE, CPUID_INTEL_ID5EDX_MWAIT_C3_SUBSTATE) FIELD_FUNC(MWAIT_C4_SUBSTATE, CPUID_INTEL_ID5EDX_MWAIT_C4_SUBSTATE) #undef FIELD_FUNC /* * Definitions of various fields' values and more complicated * macros/functions for reading cpuid fields. */ /* Effective Intel CPU Families */ #define CPUID_FAMILY_486 4 #define CPUID_FAMILY_P5 5 #define CPUID_FAMILY_P6 6 #define CPUID_FAMILY_P4 15 /* Effective AMD CPU Families */ #define CPUID_FAMILY_5x86 4 #define CPUID_FAMILY_K5 5 #define CPUID_FAMILY_K6 5 #define CPUID_FAMILY_K7 6 #define CPUID_FAMILY_K8 15 #define CPUID_FAMILY_K8L 16 #define CPUID_FAMILY_K8MOBILE 17 #define CPUID_FAMILY_EXTENDED 15 /* Intel model information */ #define CPUID_MODEL_PPRO 1 #define CPUID_MODEL_PII_03 3 #define CPUID_MODEL_PII_05 5 #define CPUID_MODEL_CELERON_06 6 #define CPUID_MODEL_PM_09 9 #define CPUID_MODEL_PM_0D 13 #define CPUID_MODEL_PM_0E 14 // Yonah / Sossaman #define CPUID_MODEL_CORE_0F 15 // Conroe / Merom #define CPUID_MODEL_CORE_17 0x17 // Penryn #define CPUID_MODEL_NEHALEM_1A 0x1a // Nehalem / Gainestown #define CPUID_MODEL_ATOM_1C 0x1c // Silverthorne / Diamondville #define CPUID_MODEL_CORE_1D 0x1d // Dunnington #define CPUID_MODEL_PIII_07 7 #define CPUID_MODEL_PIII_08 8 #define CPUID_MODEL_PIII_0A 10 /* AMD model information */ #define CPUID_MODEL_BARCELONA_02 0x02 // Barcelona (both Opteron & Phenom kind) /* *---------------------------------------------------------------------- * * CPUID_IsVendor{AMD,Intel} -- * * Determines if the vendor string in cpuid id0 is from {AMD,Intel}. * * Results: * True iff vendor string is CPUID_{AMD,INTEL}_VENDOR_STRING * * Side effects: * None. * *---------------------------------------------------------------------- */ static INLINE Bool CPUID_IsRawVendor(CPUIDRegs *id0, const char* vendor) { // hard to get strcmp() in some environments, so do it in the raw return (id0->ebx == *(const uint32 *) (vendor + 0) && id0->ecx == *(const uint32 *) (vendor + 4) && id0->edx == *(const uint32 *) (vendor + 8)); } static INLINE Bool CPUID_IsVendorAMD(CPUIDRegs *id0) { return CPUID_IsRawVendor(id0, CPUID_AMD_VENDOR_STRING); } static INLINE Bool CPUID_IsVendorIntel(CPUIDRegs *id0) { return CPUID_IsRawVendor(id0, CPUID_INTEL_VENDOR_STRING); } static INLINE uint32 CPUID_EFFECTIVE_FAMILY(uint32 v) /* %eax from CPUID with %eax=1. */ { return CPUID_FAMILY(v) + (CPUID_FAMILY(v) == CPUID_FAMILY_EXTENDED ? CPUID_EXTENDED_FAMILY(v) : 0); } /* Normally only used when FAMILY==CPUID_FAMILY_EXTENDED, but Intel is * now using the extended model field for FAMILY==CPUID_FAMILY_P6 to * refer to the newer Core2 CPUs */ static INLINE uint32 CPUID_EFFECTIVE_MODEL(uint32 v) /* %eax from CPUID with %eax=1. */ { return CPUID_MODEL(v) + (CPUID_EXTENDED_MODEL(v) << 4); } /* * Notice that CPUID families for Intel and AMD overlap. The following macros * should only be used AFTER the manufacturer has been established (through * the use of CPUID standard function 0). */ static INLINE Bool CPUID_FAMILY_IS_486(uint32 _eax) { return CPUID_EFFECTIVE_FAMILY(_eax) == CPUID_FAMILY_486; } static INLINE Bool CPUID_FAMILY_IS_P5(uint32 _eax) { return CPUID_EFFECTIVE_FAMILY(_eax) == CPUID_FAMILY_P5; } static INLINE Bool CPUID_FAMILY_IS_P6(uint32 _eax) { return CPUID_EFFECTIVE_FAMILY(_eax) == CPUID_FAMILY_P6; } static INLINE Bool CPUID_FAMILY_IS_PENTIUM4(uint32 _eax) { return CPUID_EFFECTIVE_FAMILY(_eax) == CPUID_FAMILY_P4; } /* * Intel Pentium M processors are Yonah/Sossaman or an older P-M */ static INLINE Bool CPUID_UARCH_IS_PENTIUM_M(uint32 v) // IN: %eax from CPUID with %eax=1. { /* Assumes the CPU manufacturer is Intel. */ return CPUID_FAMILY_IS_P6(v) && (CPUID_EFFECTIVE_MODEL(v) == CPUID_MODEL_PM_09 || CPUID_EFFECTIVE_MODEL(v) == CPUID_MODEL_PM_0D || CPUID_EFFECTIVE_MODEL(v) == CPUID_MODEL_PM_0E); } /* * Intel Core processors are Merom, Conroe, Woodcrest, Clovertown, * Penryn, Dunnington, Kentsfield, Yorktown, Harpertown, ........ */ static INLINE Bool CPUID_UARCH_IS_CORE(uint32 v) // IN: %eax from CPUID with %eax=1. { uint32 model = CPUID_EFFECTIVE_MODEL(v); /* Assumes the CPU manufacturer is Intel. */ return CPUID_FAMILY_IS_P6(v) && model >= CPUID_MODEL_CORE_0F && (model < CPUID_MODEL_NEHALEM_1A || model == CPUID_MODEL_CORE_1D); } /* * Intel Nehalem processors are: Nehalem, Gainestown. */ static INLINE Bool CPUID_UARCH_IS_NEHALEM(uint32 v) // IN: %eax from CPUID with %eax=1. { /* Assumes the CPU manufacturer is Intel. */ return CPUID_FAMILY_IS_P6(v) && CPUID_EFFECTIVE_MODEL(v) == CPUID_MODEL_NEHALEM_1A; } static INLINE Bool CPUID_FAMILY_IS_K7(uint32 _eax) { return CPUID_EFFECTIVE_FAMILY(_eax) == CPUID_FAMILY_K7; } static INLINE Bool CPUID_FAMILY_IS_K8(uint32 _eax) { return CPUID_EFFECTIVE_FAMILY(_eax) == CPUID_FAMILY_K8; } static INLINE Bool CPUID_FAMILY_IS_K8EXT(uint32 _eax) { /* * We check for this pattern often enough that it's * worth a separate function, for syntactic sugar. */ return CPUID_FAMILY_IS_K8(_eax) && CPUID_EXTENDED_MODEL(_eax) != 0; } static INLINE Bool CPUID_FAMILY_IS_K8L(uint32 _eax) { return CPUID_EFFECTIVE_FAMILY(_eax) == CPUID_FAMILY_K8L; } static INLINE Bool CPUID_FAMILY_IS_K8MOBILE(uint32 _eax) { /* Essentially a K8 (not K8L) part, but with mobile features. */ return CPUID_EFFECTIVE_FAMILY(_eax) == CPUID_FAMILY_K8MOBILE; } static INLINE Bool CPUID_FAMILY_IS_K8STAR(uint32 _eax) { /* * Read function name as "K8*", as in wildcard. * Matches K8 or K8L or K8MOBILE */ return CPUID_FAMILY_IS_K8(_eax) || CPUID_FAMILY_IS_K8L(_eax) || CPUID_FAMILY_IS_K8MOBILE(_eax); } /* * AMD Barcelona (of either Opteron or Phenom kind). */ static INLINE Bool CPUID_MODEL_IS_BARCELONA(uint32 v) // IN: %eax from CPUID with %eax=1. { /* Assumes the CPU manufacturer is AMD. */ return CPUID_FAMILY_IS_K8L(v) && CPUID_EFFECTIVE_MODEL(v) == CPUID_MODEL_BARCELONA_02; } #define CPUID_TYPE_PRIMARY 0 #define CPUID_TYPE_OVERDRIVE 1 #define CPUID_TYPE_SECONDARY 2 #define CPUID_INTEL_ID4EAX_CACHE_TYPE_NULL 0 #define CPUID_INTEL_ID4EAX_CACHE_TYPE_DATA 1 #define CPUID_INTEL_ID4EAX_CACHE_TYPE_INST 2 #define CPUID_INTEL_ID4EAX_CACHE_TYPE_UNIF 3 #define CPUID_INTEL_ID4EAX_CACHE_SELF_INIT 0x00000100 #define CPUID_INTEL_ID4EAX_CACHE_FULLY_ASSOC 0x00000200 /* * On AMD chips before Opteron and Intel chips before P4 model 3, * WRMSR(TSC) clears the upper half of the TSC instead of using %edx. */ static INLINE Bool CPUID_FullyWritableTSC(Bool isIntel, // IN uint32 v) // IN: %eax from CPUID with %eax=1. { /* * Returns FALSE if: * - Intel && P6 (pre-core) or * - Intel && P4 (model < 3) or * - !Intel && pre-K8 Opteron * Otherwise, returns TRUE. */ return !((isIntel && ((CPUID_FAMILY_IS_P6(v) && CPUID_EFFECTIVE_MODEL(v) < CPUID_MODEL_PM_0E) || (CPUID_FAMILY_IS_PENTIUM4(v) && CPUID_EFFECTIVE_MODEL(v) < 3))) || (!isIntel && CPUID_FAMILY(v) < CPUID_FAMILY_K8)); } /* * For certain AMD processors, an lfence instruction is necessary at various * places to ensure ordering. */ static INLINE Bool CPUID_VendorRequiresFence(CpuidVendors vendor) { return vendor == CPUID_VENDOR_AMD; } static INLINE Bool CPUID_VersionRequiresFence(uint32 version) { return CPUID_EFFECTIVE_FAMILY(version) == CPUID_FAMILY_K8 && CPUID_EFFECTIVE_MODEL(version) < 0x40; } static INLINE Bool CPUID_ID0RequiresFence(CPUIDRegs *id0) { if (id0->eax == 0) { return FALSE; } return CPUID_IsVendorAMD(id0); } static INLINE Bool CPUID_ID1RequiresFence(CPUIDRegs *id1) { return CPUID_VersionRequiresFence(id1->eax); } static INLINE Bool CPUID_RequiresFence(CpuidVendors vendor, // IN uint32 version) // IN: %eax from CPUID with %eax=1. { return CPUID_VendorRequiresFence(vendor) && CPUID_VersionRequiresFence(version); } /* *---------------------------------------------------------------------- * * CPUID_CountsCPUIDAsBranch -- * * Returns TRUE iff the cpuid given counts CPUID as a branch * (i.e. is a pre-Merom E CPU). * *---------------------------------------------------------------------- */ static INLINE Bool CPUID_CountsCPUIDAsBranch(uint32 v) /* %eax from CPUID with %eax=1 */ { /* * CPUID no longer a branch starting with Merom E. Bug 148411. * Penryn (Extended Model: 1) also has this fixed. * * Merom E is: CPUID.1.eax & 0xfff = 0x6f9 */ return !(CPUID_FAMILY_IS_P6(v) && (CPUID_EFFECTIVE_MODEL(v) > CPUID_MODEL_CORE_0F || (CPUID_EFFECTIVE_MODEL(v) == CPUID_MODEL_CORE_0F && CPUID_STEPPING(v) >= 9))); } /* * On Merom and later Intel chips, not present PDPTEs with reserved bits * set do not fault with a #GP. See PR# 109120. */ static INLINE Bool CPUID_FaultOnNPReservedPDPTE(uint32 v) // IN: %eax from CPUID with %eax=1. { return !(CPUID_FAMILY_IS_P6(v) && (CPUID_EFFECTIVE_MODEL(v) >= CPUID_MODEL_CORE_0F)); } /* * The following low-level functions compute the number of * cores per cpu. They should be used cautiously because * they do not necessarily work on all types of CPUs. * High-level functions that are correct for all CPUs are * available elsewhere: see lib/cpuidInfo/cpuidInfo.c. */ static INLINE uint32 CPUID_IntelCoresPerPackage(uint32 v) /* %eax from CPUID with %eax=4 and %ecx=0. */ { // Note: This is not guaranteed to work on older Intel CPUs. return 1 + CPUID_INTEL_CORE_COUNT(v); } static INLINE uint32 CPUID_AMDCoresPerPackage(uint32 v) /* %ecx from CPUID with %eax=0x80000008. */ { // Note: This is not guaranteed to work on older AMD CPUs. return 1 + CPUID_AMD_CORE_COUNT(v); } /* * Hypervisor CPUID space is 0x400000XX. */ static INLINE Bool CPUID_IsHypervisorLevel(uint32 level, uint32 *offset) { *offset = level & 0xff; return (level & 0xffffff00) == 0x40000000; } #endif vmhgfs-only/hgfs.h0000444000000000000000000001716112025726745013143 0ustar rootroot/********************************************************* * Copyright (C) 1998 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 * *********************************************************/ /* * hgfs.h -- * * Header file for public common data types used in the VMware * Host/Guest File System (hgfs). * * This file is included by hgfsProto.h, which defines message formats * used in the hgfs protocol, and by hgfsDev.h, which defines the * interface between the kernel and the hgfs pserver. [bac] */ #ifndef _HGFS_H_ # define _HGFS_H_ #define INCLUDE_ALLOW_USERLEVEL #define INCLUDE_ALLOW_MODULE #define INCLUDE_ALLOW_DISTRIBUTE #include "includeCheck.h" #include "vm_assert.h" /* * Maximum number of pages to transfer to/from the HGFS server for V3 protocol * operations that support large requests/replies, e.g. reads and writes. */ #define HGFS_LARGE_IO_MAX_PAGES 15 /* * Maximum allowed packet size in bytes. All hgfs code should be made * safe with respect to this limit. */ #define HGFS_PACKET_MAX 6144 /* * The HGFS_LARGE_PACKET_MAX size is used to allow guests to make * read / write requests of sizes larger than HGFS_PACKET_MAX. The larger size * can only be used with server operations that are specified to be large packet * capable in hgfsProto.h. */ #define HGFS_LARGE_PACKET_MAX ((4096 * HGFS_LARGE_IO_MAX_PAGES) + 2048) /* Maximum number of bytes to read or write to a hgfs server in a single packet. */ #define HGFS_IO_MAX 4096 /* Maximum number of bytes to read or write to a V3 server in a single hgfs packet. */ #define HGFS_LARGE_IO_MAX (HGFS_LARGE_IO_MAX_PAGES * 4096) /* * Open mode * * These are equivalent to the O_RDONLY, O_WRONLY, O_RDWR open flags * in Unix; they specify which type of access is being requested. These three * modes are mutually exclusive and one is required; all other flags are * modifiers to the mode and must come afterwards as a bitmask. Beware that * HGFS_OPEN_MODE_READ_ONLY contains the value 0 so simply masking another * variable with it to detect its presence is not safe. The _ACCMODES entry in * the enum serves as a bitmask for the others. * * Changing the order of this enum will break stuff. * * This definition is used in some places that don't include * hgfsProto.h, which is why it is here instead of there. */ typedef enum { HGFS_OPEN_MODE_READ_ONLY, HGFS_OPEN_MODE_WRITE_ONLY, HGFS_OPEN_MODE_READ_WRITE, HGFS_OPEN_MODE_ACCMODES, /* You cannot add anything else here. Really. */ } HgfsOpenMode; /* * Open flags. * * Each should be shifted left by HGFS_OPEN_MODE_READ_WRITE plus whatever flag * number they are, starting with zero. * * The sequential flag indicates that reads and writes on this handle should * not seek on each operation; instead, the system's file pointer will be used * so each operation is performed where the last one finished. This flag is * necessary when reading from or writing to non-seekable files (such as procfs * nodes on Linux) but can also lead to inconsistent results if a client shares * a handle amongst several of its callers. This flag should only be used when * the client knows the file is non-seekable and the burden of ensuring file * handles aren't shared falls upon the hgfs client, not the server. */ #define HGFS_OPEN_SEQUENTIAL (1 << HGFS_OPEN_MODE_READ_WRITE) /* Masking helpers. */ #define HGFS_OPEN_MODE_ACCMODE(mode) (mode & HGFS_OPEN_MODE_ACCMODES) #define HGFS_OPEN_MODE_FLAGS(mode) (mode & ~HGFS_OPEN_MODE_ACCMODES) #define HGFS_OPEN_MODE_IS_VALID_MODE(mode) \ (HGFS_OPEN_MODE_ACCMODE(mode) == HGFS_OPEN_MODE_READ_ONLY || \ HGFS_OPEN_MODE_ACCMODE(mode) == HGFS_OPEN_MODE_WRITE_ONLY || \ HGFS_OPEN_MODE_ACCMODE(mode) == HGFS_OPEN_MODE_READ_WRITE) /* * Return status for replies from the server. * * Changing the order of this enum will break the protocol; new status * types should be added at the end. * * This definition is used in some places that don't include * hgfsProto.h, which is why it is here instead of there. * * XXX: So we have a problem here. At some point, HGFS_STATUS_INVALID_NAME was * added to the list of errors. Later, HGFS_STATUS_GENERIC_ERROR was added, but * it was added /before/ HGFS_STATUS_INVALID_NAME. Nobody noticed because the * error codes travelled from hgfsProto.h to hgfs.h in that same change. Worse, * we GA'ed a product (Server 1.0) this way. * * XXX: I've reversed the order because otherwise new HGFS clients working * against WS55-era HGFS servers will think they got HGFS_STATUS_GENERIC_ERROR * when the server sent them HGFS_STATUS_INVALID_NAME. This was a problem * the Linux client converts HGFS_STATUS_GENERIC_ERROR to -EIO, which causes * HgfsLookup to fail unexpectedly (normally HGFS_STATUS_INVALID_NAME is * converted to -ENOENT, an expected result in HgfsLookup). */ typedef enum { HGFS_STATUS_SUCCESS, HGFS_STATUS_NO_SUCH_FILE_OR_DIR, HGFS_STATUS_INVALID_HANDLE, HGFS_STATUS_OPERATION_NOT_PERMITTED, HGFS_STATUS_FILE_EXISTS, HGFS_STATUS_NOT_DIRECTORY, HGFS_STATUS_DIR_NOT_EMPTY, HGFS_STATUS_PROTOCOL_ERROR, HGFS_STATUS_ACCESS_DENIED, HGFS_STATUS_INVALID_NAME, HGFS_STATUS_GENERIC_ERROR, HGFS_STATUS_SHARING_VIOLATION, HGFS_STATUS_NO_SPACE, HGFS_STATUS_OPERATION_NOT_SUPPORTED, HGFS_STATUS_NAME_TOO_LONG, } HgfsStatus; /* * HGFS RPC commands * * HGFS servers can run in a variety of places across several different * transport layers. These definitions constitute all known RPC commands. * * For each definition, there is both the server string (the command itself) * as well as a client "prefix", which is the command followed by a space. * This is provided for convenience, since clients will need to copy both * the command and the space into some buffer that is then sent over the * backdoor. * * In Host --> Guest RPC traffic, the host endpoint is TCLO and the guest * endpoint is RpcIn. TCLO is a particularly confusing name choice which dates * back to when the host was to send raw TCL code to the guest (TCL Out == * TCLO). * * In Guest --> Host RPC traffic, the guest endpoint is RpcOut and the host * endpoint is RPCI. */ /* * When an RPCI listener registers for this command, HGFS requests are expected * to be synchronously sent from the guest and replies are expected to be * synchronously returned. * * When an RpcIn listener registers for this command, requests are expected to * be asynchronously sent from the host and synchronously returned from the * guest. * * In short, an endpoint sending this command is sending a request whose reply * should be returned synchronously. */ #define HGFS_SYNC_REQREP_CMD "f" #define HGFS_SYNC_REQREP_CLIENT_CMD HGFS_SYNC_REQREP_CMD " " #define HGFS_SYNC_REQREP_CLIENT_CMD_LEN (sizeof HGFS_SYNC_REQREP_CLIENT_CMD - 1) /* * This is just for the sake of macro naming. Since we are guaranteed * equal command lengths, defining command length via a generalized macro name * will prevent confusion. */ #define HGFS_CLIENT_CMD_LEN HGFS_SYNC_REQREP_CLIENT_CMD_LEN #endif // _HGFS_H_ vmhgfs-only/backdoor_def.h0000444000000000000000000001375312025726745014621 0ustar rootroot/********************************************************* * Copyright (C) 1998 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 * *********************************************************/ /* * backdoor_def.h -- * * This contains backdoor defines that can be included from * an assembly language file. */ #ifndef _BACKDOOR_DEF_H_ #define _BACKDOOR_DEF_H_ #define INCLUDE_ALLOW_MODULE #define INCLUDE_ALLOW_USERLEVEL #define INCLUDE_ALLOW_VMMEXT #define INCLUDE_ALLOW_VMCORE #define INCLUDE_ALLOW_VMKERNEL #include "includeCheck.h" /* * If you want to add a new low-level backdoor call for a guest userland * application, please consider using the GuestRpc mechanism instead. --hpreg */ #define BDOOR_MAGIC 0x564D5868 /* Low-bandwidth backdoor port. --hpreg */ #define BDOOR_PORT 0x5658 #define BDOOR_CMD_GETMHZ 1 /* * BDOOR_CMD_APMFUNCTION is used by: * * o The FrobOS code, which instead should either program the virtual chipset * (like the new BIOS code does, matthias offered to implement that), or not * use any VM-specific code (which requires that we correctly implement * "power off on CLI HLT" for SMP VMs, boris offered to implement that) * * o The old BIOS code, which will soon be jettisoned * * --hpreg */ #define BDOOR_CMD_APMFUNCTION 2 #define BDOOR_CMD_GETDISKGEO 3 #define BDOOR_CMD_GETPTRLOCATION 4 #define BDOOR_CMD_SETPTRLOCATION 5 #define BDOOR_CMD_GETSELLENGTH 6 #define BDOOR_CMD_GETNEXTPIECE 7 #define BDOOR_CMD_SETSELLENGTH 8 #define BDOOR_CMD_SETNEXTPIECE 9 #define BDOOR_CMD_GETVERSION 10 #define BDOOR_CMD_GETDEVICELISTELEMENT 11 #define BDOOR_CMD_TOGGLEDEVICE 12 #define BDOOR_CMD_GETGUIOPTIONS 13 #define BDOOR_CMD_SETGUIOPTIONS 14 #define BDOOR_CMD_GETSCREENSIZE 15 #define BDOOR_CMD_MONITOR_CONTROL 16 #define BDOOR_CMD_GETHWVERSION 17 #define BDOOR_CMD_OSNOTFOUND 18 #define BDOOR_CMD_GETUUID 19 #define BDOOR_CMD_GETMEMSIZE 20 #define BDOOR_CMD_HOSTCOPY 21 /* Devel only */ /* BDOOR_CMD_GETOS2INTCURSOR, 22, is very old and defunct. Reuse. */ #define BDOOR_CMD_GETTIME 23 /* Deprecated. Use GETTIMEFULL. */ #define BDOOR_CMD_STOPCATCHUP 24 #define BDOOR_CMD_PUTCHR 25 /* Devel only */ #define BDOOR_CMD_ENABLE_MSG 26 /* Devel only */ #define BDOOR_CMD_GOTO_TCL 27 /* Devel only */ #define BDOOR_CMD_INITPCIOPROM 28 #define BDOOR_CMD_INT13 29 #define BDOOR_CMD_MESSAGE 30 #define BDOOR_CMD_RSVD0 31 #define BDOOR_CMD_RSVD1 32 #define BDOOR_CMD_RSVD2 33 #define BDOOR_CMD_ISACPIDISABLED 34 #define BDOOR_CMD_TOE 35 /* Not in use */ /* BDOOR_CMD_INITLSIOPROM, 36, was merged with 28. Reuse. */ #define BDOOR_CMD_PATCH_SMBIOS_STRUCTS 37 #define BDOOR_CMD_MAPMEM 38 /* Devel only */ #define BDOOR_CMD_ABSPOINTER_DATA 39 #define BDOOR_CMD_ABSPOINTER_STATUS 40 #define BDOOR_CMD_ABSPOINTER_COMMAND 41 #define BDOOR_CMD_TIMER_SPONGE 42 #define BDOOR_CMD_PATCH_ACPI_TABLES 43 /* Catch-all to allow synchronous tests */ #define BDOOR_CMD_DEVEL_FAKEHARDWARE 44 /* Debug only - needed in beta */ #define BDOOR_CMD_GETHZ 45 #define BDOOR_CMD_GETTIMEFULL 46 #define BDOOR_CMD_STATELOGGER 47 #define BDOOR_CMD_CHECKFORCEBIOSSETUP 48 #define BDOOR_CMD_LAZYTIMEREMULATION 49 #define BDOOR_CMD_BIOSBBS 50 #define BDOOR_CMD_VASSERT 51 #define BDOOR_CMD_ISGOSDARWIN 52 #define BDOOR_CMD_DEBUGEVENT 53 /* BDOOR_CMD_OSNOTMACOSXSERVER, not crossported to this branch yet. */ #define BDOOR_CMD_GETTIMEFULL_WITH_LAG 55 #define BDOOR_CMD_ACPI_HOTPLUG_DEVICE 56 #define BDOOR_CMD_ACPI_HOTPLUG_MEMORY 57 #define BDOOR_CMD_ACPI_HOTPLUG_CBRET 58 #define BDOOR_CMD_MAX 59 /* * IMPORTANT NOTE: When modifying the behavior of an existing backdoor command, * you must adhere to the semantics expected by the oldest Tools who use that * command. Specifically, do not alter the way in which the command modifies * the registers. Otherwise backwards compatibility will suffer. */ /* High-bandwidth backdoor port. --hpreg */ #define BDOORHB_PORT 0x5659 #define BDOORHB_CMD_MESSAGE 0 #define BDOORHB_CMD_VASSERT 1 #define BDOORHB_CMD_MAX 2 /* * There is another backdoor which allows access to certain TSC-related * values using otherwise illegal PMC indices when the pseudo_perfctr * control flag is set. */ #define BDOOR_PMC_HW_TSC 0x10000 #define BDOOR_PMC_REAL_NS 0x10001 #define BDOOR_PMC_APPARENT_NS 0x10002 #define IS_BDOOR_PMC(index) (((index) | 3) == 0x10003) #define BDOOR_CMD(ecx) ((ecx) & 0xffff) #ifdef VMM /* *---------------------------------------------------------------------- * * Backdoor_CmdRequiresFullyValidVCPU -- * * A few backdoor commands require the full VCPU to be valid * (including GDTR, IDTR, TR and LDTR). The rest get read/write * access to GPRs and read access to Segment registers (selectors). * * Result: * True iff VECX contains a command that require the full VCPU to * be valid. * *---------------------------------------------------------------------- */ static INLINE Bool Backdoor_CmdRequiresFullyValidVCPU(unsigned cmd) { return cmd == BDOOR_CMD_RSVD0 || cmd == BDOOR_CMD_RSVD1 || cmd == BDOOR_CMD_RSVD2; } #endif #endif vmhgfs-only/backdoor_types.h0000444000000000000000000000672712025726745015232 0ustar rootroot/********************************************************* * Copyright (C) 1999 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 * *********************************************************/ /* * backdoor_types.h -- * * Type definitions for backdoor interaction code. */ #ifndef _BACKDOOR_TYPES_H_ #define _BACKDOOR_TYPES_H_ #ifndef VM_I386 #error The backdoor protocol is only supported on x86 architectures. #endif /* * These #defines are intended for defining register structs as part of * existing named unions. If the union should encapsulate the register * (and nothing else), use DECLARE_REG_NAMED_STRUCT defined below. */ #define DECLARE_REG32_STRUCT \ struct { \ uint16 low; \ uint16 high; \ } halfs; \ uint32 word #define DECLARE_REG64_STRUCT \ DECLARE_REG32_STRUCT; \ struct { \ uint32 low; \ uint32 high; \ } words; \ uint64 quad #ifndef VM_X86_64 #define DECLARE_REG_STRUCT DECLARE_REG32_STRUCT #else #define DECLARE_REG_STRUCT DECLARE_REG64_STRUCT #endif #define DECLARE_REG_NAMED_STRUCT(_r) \ union { DECLARE_REG_STRUCT; } _r /* * Some of the registers are expressed by semantic name, because if they were * expressed as register structs declared above, we could only address them * by fixed size (half-word, word, quad, etc.) instead of by varying size * (size_t, uintptr_t). * * To be cleaner, these registers are expressed ONLY by semantic name, * rather than by a union of the semantic name and a register struct. */ typedef union { struct { DECLARE_REG_NAMED_STRUCT(ax); size_t size; /* Register bx. */ DECLARE_REG_NAMED_STRUCT(cx); DECLARE_REG_NAMED_STRUCT(dx); DECLARE_REG_NAMED_STRUCT(si); DECLARE_REG_NAMED_STRUCT(di); } in; struct { DECLARE_REG_NAMED_STRUCT(ax); DECLARE_REG_NAMED_STRUCT(bx); DECLARE_REG_NAMED_STRUCT(cx); DECLARE_REG_NAMED_STRUCT(dx); DECLARE_REG_NAMED_STRUCT(si); DECLARE_REG_NAMED_STRUCT(di); } out; } Backdoor_proto; typedef union { struct { DECLARE_REG_NAMED_STRUCT(ax); DECLARE_REG_NAMED_STRUCT(bx); size_t size; /* Register cx. */ DECLARE_REG_NAMED_STRUCT(dx); uintptr_t srcAddr; /* Register si. */ uintptr_t dstAddr; /* Register di. */ DECLARE_REG_NAMED_STRUCT(bp); } in; struct { DECLARE_REG_NAMED_STRUCT(ax); DECLARE_REG_NAMED_STRUCT(bx); DECLARE_REG_NAMED_STRUCT(cx); DECLARE_REG_NAMED_STRUCT(dx); DECLARE_REG_NAMED_STRUCT(si); DECLARE_REG_NAMED_STRUCT(di); DECLARE_REG_NAMED_STRUCT(bp); } out; } Backdoor_proto_hb; MY_ASSERTS(BACKDOOR_STRUCT_SIZES, ASSERT_ON_COMPILE(sizeof(Backdoor_proto) == 6 * sizeof(uintptr_t)); ASSERT_ON_COMPILE(sizeof(Backdoor_proto_hb) == 7 * sizeof(uintptr_t)); ) #undef DECLARE_REG_STRUCT #endif /* _BACKDOOR_TYPES_H_ */ vmhgfs-only/guest_msg_def.h0000444000000000000000000000567612025726745015037 0ustar rootroot/********************************************************* * Copyright (C) 1998 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 * *********************************************************/ /* * guest_msg_def.h -- * * Second layer of the internal communication channel between guest * applications and vmware * */ #ifndef _GUEST_MSG_DEF_H_ #define _GUEST_MSG_DEF_H_ #define INCLUDE_ALLOW_MODULE #define INCLUDE_ALLOW_USERLEVEL #define INCLUDE_ALLOW_VMMEXT #include "includeCheck.h" /* Basic request types */ typedef enum { MESSAGE_TYPE_OPEN, MESSAGE_TYPE_SENDSIZE, MESSAGE_TYPE_SENDPAYLOAD, MESSAGE_TYPE_RECVSIZE, MESSAGE_TYPE_RECVPAYLOAD, MESSAGE_TYPE_RECVSTATUS, MESSAGE_TYPE_CLOSE, } MessageType; /* Reply statuses */ /* The basic request succeeded */ #define MESSAGE_STATUS_SUCCESS 0x0001 /* vmware has a message available for its party */ #define MESSAGE_STATUS_DORECV 0x0002 /* The channel has been closed */ #define MESSAGE_STATUS_CLOSED 0x0004 /* vmware removed the message before the party fetched it */ #define MESSAGE_STATUS_UNSENT 0x0008 /* A checkpoint occurred */ #define MESSAGE_STATUS_CPT 0x0010 /* An underlying device is powering off */ #define MESSAGE_STATUS_POWEROFF 0x0020 /* vmware has detected a timeout on the channel */ #define MESSAGE_STATUS_TIMEOUT 0x0040 /* vmware supports high-bandwidth for sending and receiving the payload */ #define MESSAGE_STATUS_HB 0x0080 /* * This mask defines the status bits that the guest is allowed to set; * we use this to mask out all other bits when receiving the status * from the guest. Otherwise, the guest can manipulate VMX state by * setting status bits that are only supposed to be changed by the * VMX. See bug 45385. */ #define MESSAGE_STATUS_GUEST_MASK MESSAGE_STATUS_SUCCESS /* * Max number of channels. * Unfortunately this has to be public because the monitor part * of the backdoor needs it for its trivial-case optimization. [greg] */ #define GUESTMSG_MAX_CHANNEL 8 /* Flags to open a channel. --hpreg */ #define GUESTMSG_FLAG_COOKIE 0x80000000 #define GUESTMSG_FLAG_ALL GUESTMSG_FLAG_COOKIE /* * Maximum size of incoming message. This is to prevent denial of host service * attacks from guest applications. */ #define GUESTMSG_MAX_IN_SIZE (64 * 1024) #endif /* _GUEST_MSG_DEF_H_ */ vmhgfs-only/cpName.c0000444000000000000000000002741512025726745013415 0ustar rootroot/********************************************************* * Copyright (C) 1998 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 * *********************************************************/ /* * cpName.c -- * * Shared portions of cross-platform name conversion routines used * by hgfs. [bac] * */ #ifdef sun #include #endif #include "cpName.h" #include "cpNameInt.h" #include "vm_assert.h" /* *---------------------------------------------------------------------- * * CPName_GetComponentGeneric -- * * Get the next component of the CP name. * * Returns the length of the component starting with the begin * pointer, and a pointer to the next component in the buffer, if * any. The "next" pointer is set to "end" if there is no next * component. * * 'illegal' is a string of characters that are not allowed to * be present in the pre-converted CP name. * * Results: * length (not including NUL termination) >= 0 of next * component on success. * error < 0 on failure (invalid component). * * Side effects: * None * *---------------------------------------------------------------------- */ int CPName_GetComponentGeneric(char const *begin, // IN: Beginning of buffer char const *end, // IN: End of buffer char const *illegal, // IN: Illegal characters char const **next) // OUT: Start of next component { char const *walk; char const *myNext; size_t len; ASSERT(begin); ASSERT(end); ASSERT(next); ASSERT(illegal); ASSERT(begin <= end); for (walk = begin; ; walk++) { if (walk == end) { /* End of buffer. No NUL was found */ myNext = end; break; } if (*walk == '\0') { /* Found a NUL */ if (walk == begin) { Log("CPName_GetComponentGeneric: error: first char can't be NUL\n"); return -1; } myNext = walk + 1; if (myNext == end) { /* Last character in the buffer is not allowed to be NUL */ return -1; } break; } /* * Make sure the input buffer does not contain any illegal * characters. In particular, we want to make sure that there * are no path separator characters in the name. Since the * cross-platform name format by definition does not use path * separators, this is an error condition, and is likely the * sign of an attack. See bug 27926. [bac] * * The test above ensures that *walk != NUL here, so we don't * need to test it again before calling strchr(). */ if (strchr(illegal, *walk) != NULL) { Log("CPName_GetComponentGeneric: error: Illegal char \"%c\" found in " "input\n", *walk); return -1; } } len = walk - begin; /* * We're only interested in looking for dot/dotdot if the illegal character * string isn't empty. These characters are only relevant when the resulting * string is to be passed down to the filesystem. Some callers (such as the * HGFS server, when dealing with actual filenames) do care about this * validation, but others (like DnD, hgFileCopy, and the HGFS server when * converting share names) just want to convert a CPName down to a * nul-terminated string. */ if (strcmp(illegal, "") != 0 && ((len == 1 && memcmp(begin, ".", 1) == 0) || (len == 2 && memcmp(begin, "..", 2) == 0))) { Log("CPName_GetComponentGeneric: error: found dot/dotdot\n"); return -1; } *next = myNext; return ((int) len); } /* *---------------------------------------------------------------------- * * CPNameConvertFrom -- * * Converts a cross-platform name representation into a string for * use in the local filesystem. This is a cross-platform * implementation and takes the path separator argument as an * argument. The path separator is prepended before each additional * path component, so this function never adds a trailing path * separator. * * Results: * 0 on success. * error < 0 on failure (the converted string did not fit in * the buffer provided or the input was invalid). * * Side effects: * None * *---------------------------------------------------------------------- */ int CPNameConvertFrom(char const **bufIn, // IN/OUT: Input to convert size_t *inSize, // IN/OUT: Size of input size_t *outSize, // IN/OUT: Size of output buffer char **bufOut, // IN/OUT: Output buffer char pathSep) // IN: Path separator character { char const *in; char const *inEnd; size_t myOutSize; char *out; ASSERT(bufIn); ASSERT(inSize); ASSERT(outSize); ASSERT(bufOut); in = *bufIn; inEnd = in + *inSize; myOutSize = *outSize; out = *bufOut; for (;;) { char const *next; int len; int newLen; len = CPName_GetComponent(in, inEnd, &next); if (len < 0) { Log("CPNameConvertFrom: error: get next component failed\n"); return len; } if (len == 0) { /* No more component */ break; } newLen = ((int) myOutSize) - len - 1; if (newLen < 0) { Log("CPNameConvertFrom: error: not enough room\n"); return -1; } myOutSize = (size_t) newLen; *out++ = pathSep; memcpy(out, in, len); out += len; in = next; } /* NUL terminate */ if (myOutSize < 1) { Log("CPNameConvertFrom: error: not enough room\n"); return -1; } *out = '\0'; /* Path name size should not require more than 4 bytes. */ ASSERT((in - *bufIn) <= 0xFFFFFFFF); /* Update pointers. */ *inSize -= (in - *bufIn); *outSize = myOutSize; *bufIn = in; *bufOut = out; return 0; } /* *---------------------------------------------------------------------------- * * CPName_Print -- * * Converts a CPName formatted string to a valid, NUL-terminated string by * replacing all embedded NUL characters with '|'. * * Results: * Pointer to a static buffer containing the converted string. * * Side effects: * None. * *---------------------------------------------------------------------------- */ char const * CPName_Print(char const *in, // IN: Name to print size_t size) // IN: Size of name { /* Static so it does not go on a kernel stack --hpreg */ static char out[128]; size_t i; ASSERT(in); ASSERT(sizeof out >= 4); if (size > sizeof out - 1) { size = sizeof out - 4; out[size] = '.'; out[size + 1] = '.'; out[size + 2] = '.'; out[size + 3] = '\0'; } else { out[size] = '\0'; } for (i = 0; i < size; i++) { out[i] = in[i] != '\0' ? in[i] : '|'; } return out; } /* *---------------------------------------------------------------------------- * * CPName_LinuxConvertTo -- * * Wrapper function that calls CPNameConvertTo() with the correct arguments * for Linux path conversions. * * Makes a cross-platform name representation from the Linux path input * string and writes it into the output buffer. * * Results: * On success, returns the number of bytes used in the cross-platform name, * NOT including the final terminating NUL character. On failure, returns * a negative error. * * Side effects: * None. * *---------------------------------------------------------------------------- */ int CPName_LinuxConvertTo(char const *nameIn, // IN: Buf to convert size_t bufOutSize, // IN: Size of the output buffer char *bufOut) // OUT: Output buffer { return CPNameConvertTo(nameIn, bufOutSize, bufOut, '/', NULL); } /* *---------------------------------------------------------------------------- * * CPName_WindowsConvertTo -- * * Wrapper function that calls CPNameConvertTo() with the correct arguments * for Windows path conversions. * * Makes a cross-platform name representation from the Linux path input * string and writes it into the output buffer. * * Results: * On success, returns the number of bytes used in the cross-platform name, * NOT including the final terminating NUL character. On failure, returns * a negative error. * * Side effects: * None. * *---------------------------------------------------------------------------- */ int CPName_WindowsConvertTo(char const *nameIn, // IN: Buf to convert size_t bufOutSize, // IN: Size of the output buffer char *bufOut) // OUT: Output buffer { return CPNameConvertTo(nameIn, bufOutSize, bufOut, '\\', ":"); } /* *---------------------------------------------------------------------- * * CPNameConvertTo -- * * Makes a cross-platform name representation from the input string * and writes it into the output buffer. * * Results: * On success, returns the number of bytes used in the * cross-platform name, NOT including the final terminating NUL * character. On failure, returns a negative error. * * Side effects: * None * *---------------------------------------------------------------------- */ int CPNameConvertTo(char const *nameIn, // IN: Buf to convert size_t bufOutSize, // IN: Size of the output buffer char *bufOut, // OUT: Output buffer char pathSep, // IN: path separator to use char *ignores) // IN: chars to not transfer to output { char const *origOut = bufOut; char const *endOut = bufOut + bufOutSize; size_t cpNameLength = 0; ASSERT(nameIn); ASSERT(bufOut); /* Skip any path separators at the beginning of the input string */ while (*nameIn == pathSep) { nameIn++; } /* * Copy the string to the output buf, converting all path separators into * '\0' and ignoring the specified characters. */ for (; *nameIn != '\0' && bufOut < endOut; nameIn++) { if (ignores) { char *currIgnore = ignores; Bool ignore = FALSE; while (*currIgnore != '\0') { if (*nameIn == *currIgnore) { ignore = TRUE; break; } currIgnore++; } if (!ignore) { *bufOut = (*nameIn == pathSep) ? '\0' : *nameIn; bufOut++; } } else { *bufOut = (*nameIn == pathSep) ? '\0' : *nameIn; bufOut++; } } /* * NUL terminate. XXX This should go away. * * When we get rid of NUL termination here, this test should * also change to "if (*nameIn != '\0')". */ if (bufOut == endOut) { return -1; } *bufOut = '\0'; /* Path name size should not require more than 4 bytes. */ ASSERT((bufOut - origOut) <= 0xFFFFFFFF); /* If there were any trailing path separators, dont count them [krishnan] */ cpNameLength = bufOut - origOut; while ((cpNameLength >= 1) && (origOut[cpNameLength - 1] == 0)) { cpNameLength--; } /* Return number of bytes used */ return (int) cpNameLength; } vmhgfs-only/cpNameLinux.c0000444000000000000000000001320312025726745014423 0ustar rootroot/********************************************************* * Copyright (C) 1998 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 * *********************************************************/ /* * cpNameLinux.c -- * * Linux implementation of cross-platform name conversion * routines used by hgfs. [bac] * */ #if defined(sun) && defined(SOL10) #include #endif #include "cpName.h" #include "cpNameInt.h" #include "vm_assert.h" /* *---------------------------------------------------------------------- * * CPName_GetComponent -- * * Get the next component of the CP name. * * Returns the length of the component starting with the begin * pointer, and a pointer to the next component in the buffer, if * any. The "next" pointer is set to "end" if there is no next * component. * * Results: * length (not including NUL termination) >= 0 of next * component on success. * error < 0 on failure (invalid component). * * Side effects: * None * *---------------------------------------------------------------------- */ int CPName_GetComponent(char const *begin, // IN: Beginning of buffer char const *end, // IN: End of buffer char const **next) // OUT: Start of next component { ASSERT(begin); ASSERT(end); ASSERT(next); /* * '/' is not a legal character on Linux, since it is a path * separator. */ return CPName_GetComponentGeneric(begin, end, "/", next); } /* *---------------------------------------------------------------------- * * CPName_ConvertFrom -- * * Converts a cross-platform name representation into a string for * use in the local filesystem. * * Results: * Length (not including NUL termination) >= 0 of resulting * string on success. * Negative error on failure (the converted string did not fit in * the buffer provided or the input was invalid). * * Side effects: * None * *---------------------------------------------------------------------- */ int CPName_ConvertFrom(char const **bufIn, // IN/OUT: Input to convert size_t *inSize, // IN/OUT: Size of input size_t *outSize, // IN/OUT: Size of output buffer char **bufOut) // IN/OUT: Output buffer { ASSERT(bufIn); ASSERT(inSize); ASSERT(outSize); ASSERT(bufOut); return CPNameConvertFrom(bufIn, inSize, outSize, bufOut, '/'); } /* *---------------------------------------------------------------------- * * CPName_ConvertFromRoot -- * * Append the appropriate prefix to the output buffer for accessing * the root of the local filesystem. CPName_ConvertFrom prepends * leading path separators before each path component, but only * when the next component has nonzero length, so we still need to * special case this for Linux. * * The pointers and sizes are updated appropriately. * * Results: * Status of name conversion * * Side effects: * None * *---------------------------------------------------------------------- */ HgfsNameStatus CPName_ConvertFromRoot(char const **bufIn, // IN/OUT: Input to convert size_t *inSize, // IN/OUT: Size of input size_t *outSize, // IN/OUT: Size of output buffer char **bufOut) // IN/OUT: Output buffer { char const *next; char *out; int len; ASSERT(bufIn); ASSERT(inSize); ASSERT(outSize); ASSERT(bufOut); out = *bufOut; /* * Get first component */ len = CPName_GetComponent(*bufIn, *bufIn + *inSize, &next); if (len < 0) { Log("CPName_ConvertFromRoot: get first component failed\n"); return HGFS_NAME_STATUS_FAILURE; } /* Space for leading '/' plus NUL termination */ if (*outSize < len + 2) { return HGFS_NAME_STATUS_FAILURE; } /* Put a leading '/' in the output buffer either way */ *out++ = '/'; memcpy(out, *bufIn, len); out += len; /* NUL terminate */ *out = '\0'; *inSize -= next - *bufIn; *outSize -= out - *bufOut; *bufIn = next; *bufOut = out; return HGFS_NAME_STATUS_COMPLETE; } /* *---------------------------------------------------------------------------- * * CPName_ConvertTo -- * * Wrapper function that calls the Linux implementation of _ConvertTo(). * * Makes a cross-platform name representation from the Linux path input * string and writes it into the output buffer. * * Results: * On success, returns the number of bytes used in the * cross-platform name, NOT including the final terminating NUL * character. On failure, returns a negative error. * * Side effects: * None. * *---------------------------------------------------------------------------- */ int CPName_ConvertTo(char const *nameIn, // IN: Buf to convert size_t bufOutSize, // IN: Size of the output buffer char *bufOut) // OUT: Output buffer { return CPName_LinuxConvertTo(nameIn, bufOutSize, bufOut); } vmhgfs-only/cpNameLite.c0000444000000000000000000000512212025726745014222 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ /* * cpNameLite.c -- * * Shared portions of cross-platform name conversion routines used * by hgfs. Unlike the real CP name conversion routines, these ones * just convert path separators to nul characters and vice versa. * */ #include "cpNameLite.h" #include "vm_assert.h" /* *---------------------------------------------------------------------- * * CPNameLite_ConvertTo -- * * Makes a cross-platform lite name representation from the input * string. * * Results: * None * * Side effects: * None * *---------------------------------------------------------------------- */ void CPNameLite_ConvertTo(char *bufIn, // IN/OUT: Input to convert size_t inSize, // IN: Size of input buffer char pathSep) // IN: Path separator { size_t pos; ASSERT(bufIn); for (pos = 0; pos < inSize; pos++) { if (bufIn[pos] == pathSep) { bufIn[pos] = '\0'; } } } /* *---------------------------------------------------------------------- * * CPNameLite_ConvertFrom -- * * Converts a cross-platform lite name representation into a string for * use in the local filesystem. This is a cross-platform * implementation and takes the path separator as an * argument. * * Results: * None * * Side effects: * None * *---------------------------------------------------------------------- */ void CPNameLite_ConvertFrom(char *bufIn, // IN/OUT: Input to convert size_t inSize, // IN: Size of input buffer char pathSep) // IN: Path separator { size_t pos; ASSERT(bufIn); for (pos = 0; pos < inSize; pos++) { if (bufIn[pos] == '\0') { bufIn[pos] = pathSep; } } } vmhgfs-only/hgfsDevLinux.h0000444000000000000000000000474712025726745014630 0ustar rootroot/********************************************************* * Copyright (C) 1998 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 * *********************************************************/ /* * hgfsDev.h -- * * Header for code shared between the hgfs linux kernel module driver * and the pserver. */ #ifndef _HGFS_DEV_H_ #define _HGFS_DEV_H_ #include "vm_basic_types.h" #include "hgfs.h" #define HGFS_NAME "vmhgfs" // Name of FS (e.g. "mount -t vmhgfs") #define HGFS_DEVICE_NAME "dev" // Name of our device under /proc/fs/HGFS_NAME/ #define HGFS_SUPER_MAGIC 0xbacbacbc // Superblock magic number #define HGFS_PROTOCOL_VERSION 1 // Incremented when something changes #define HGFS_DEFAULT_TTL 1 // Default TTL for dentries /* * Mount information, passed from pserver process to kernel * at mount time. * * XXX: I'm hijacking this struct. In the future, when the Solaris HGFS driver * loses its pserver, the struct will be used by /sbin/mount.vmhgfs solely. * As is, it is also used by the Solaris pserver. */ typedef struct HgfsMountInfo { uint32 magicNumber; // hgfs magic number uint32 version; // protocol version uint32 fd; // file descriptor of client file #ifndef sun uid_t uid; // desired owner of files Bool uidSet; // is the owner actually set? gid_t gid; // desired group of files Bool gidSet; // is the group actually set? unsigned short fmask; // desired file mask unsigned short dmask; // desired directory mask uint32 ttl; // number of seconds before revalidating dentries const char *shareNameHost; // must be ".host" const char *shareNameDir; // desired share name for mounting #endif } HgfsMountInfo; #endif //ifndef _HGFS_DEV_H_ vmhgfs-only/hgfsUtil.h0000444000000000000000000000723112025726745013776 0ustar rootroot/********************************************************* * Copyright (C) 1998 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 * *********************************************************/ /* * hgfsUtil.h -- * * Utility functions and macros used by hgfs. */ #ifndef _HGFSUTIL_H_ # define _HGFSUTIL_H_ # if defined(__linux__) && defined(__KERNEL__) # include "driver-config.h" # include // for time_t and timespec /* Include time.h in userspace code, but not in Solaris kernel code. */ # elif defined(__FreeBSD__) && defined(_KERNEL) /* Do nothing. */ # elif defined(__APPLE__) && defined(KERNEL) # include # else # include # endif # include "vm_basic_types.h" # if !defined(_STRUCT_TIMESPEC) && \ !defined(_TIMESPEC_DECLARED) && \ !defined(__timespec_defined) && \ !defined(sun) && \ !defined(__FreeBSD__) && \ !__APPLE__ && \ !defined(_WIN32) struct timespec { time_t tv_sec; long tv_nsec; }; # endif # include "hgfs.h" /* Cross-platform representation of a platform-specific error code. */ #ifndef _WIN32 # if defined(__KERNEL__) || defined(_KERNEL) || defined(KERNEL) # if defined(__linux__) # include # elif defined(sun) || defined(__FreeBSD__) || defined(__APPLE__) # include # endif # else # include # endif typedef int HgfsInternalStatus; #else # include typedef DWORD HgfsInternalStatus; #endif /* * Unfortunately, we need a catch-all "generic error" to use with * HgfsInternalStatus, because there are times when cross-platform code needs * to return its own errors along with errors from platform specific code. * * Using -1 should be safe because we expect our platforms to use zero as * success and a positive range of numbers as error values. */ #define HGFS_INTERNAL_STATUS_ERROR -1 /* * FreeBSD (pre-6.0) does not define EPROTO, so we'll define our own error code. */ #if defined(__FreeBSD__) && !defined(EPROTO) #define EPROTO (ELAST + 1) #endif #define HGFS_NAME_BUFFER_SIZE(request) (HGFS_PACKET_MAX - (sizeof *request - 1)) #define HGFS_NAME_BUFFER_SIZET(sizet) (HGFS_PACKET_MAX - (sizet) - 1) #ifndef _WIN32 /* * Routines for converting between Win NT and unix time formats. The * hgfs attributes use the NT time formats, so the linux driver and * server have to convert back and forth. [bac] */ uint64 HgfsConvertToNtTime(time_t unixTime, // IN long nsec); // IN static INLINE uint64 HgfsConvertTimeSpecToNtTime(const struct timespec *unixTime) // IN { return HgfsConvertToNtTime(unixTime->tv_sec, unixTime->tv_nsec); } int HgfsConvertFromNtTime(time_t * unixTime, // OUT uint64 ntTime); // IN int HgfsConvertFromNtTimeNsec(struct timespec *unixTime, // OUT uint64 ntTime); // IN #endif /* !def(_WIN32) */ HgfsStatus HgfsConvertFromInternalStatus(HgfsInternalStatus status); // IN #endif /* _HGFSUTIL_H_ */ vmhgfs-only/cpName.h0000444000000000000000000001011612025726745013410 0ustar rootroot/********************************************************* * Copyright (C) 1998 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 * *********************************************************/ /* * cpName.h -- * * Cross-platform name format used by hgfs. * */ #ifndef __CP_NAME_H__ #define __CP_NAME_H__ #ifdef __KERNEL__ # include "driver-config.h" # include #elif defined(__FreeBSD__) # if defined(_KERNEL) # include # define strchr(s,c) index(s,c) # else # include # endif #elif defined(__APPLE__) && defined(KERNEL) # include #elif !defined(sun) # include # include #endif #include "vm_basic_types.h" /* Status codes for processing share names */ typedef enum { HGFS_NAME_STATUS_COMPLETE, /* Name is complete */ HGFS_NAME_STATUS_FAILURE, /* Name processing failed */ HGFS_NAME_STATUS_INCOMPLETE_BASE, /* Name is base of namespace */ HGFS_NAME_STATUS_INCOMPLETE_ROOT, /* Name is "root" only */ HGFS_NAME_STATUS_INCOMPLETE_DRIVE, /* Name is "root drive" only */ HGFS_NAME_STATUS_INCOMPLETE_UNC, /* Name is "root unc" only */ HGFS_NAME_STATUS_INCOMPLETE_UNC_MACH, /* Name is "root unc " only */ HGFS_NAME_STATUS_DOES_NOT_EXIST, /* Name does not exist */ HGFS_NAME_STATUS_ACCESS_DENIED, /* Desired access to share denied */ HGFS_NAME_STATUS_SYMBOLIC_LINK, /* Name contains a symbolic link */ HGFS_NAME_STATUS_OUT_OF_MEMORY, /* Out of memory while processing */ HGFS_NAME_STATUS_TOO_LONG, /* Name has overly long component */ } HgfsNameStatus; int CPName_ConvertTo(char const *nameIn, // IN: The buf to convert size_t bufOutSize, // IN: The size of the output buffer char *bufOut); // OUT: The output buffer int CPName_LinuxConvertTo(char const *nameIn, // IN: buf to convert size_t bufOutSize, // IN: size of the output buffer char *bufOut); // OUT: output buffer int CPName_WindowsConvertTo(char const *nameIn, // IN: buf to convert size_t bufOutSize, // IN: size of the output buffer char *bufOut); // OUT: output buffer int CPName_ConvertFrom(char const **bufIn, // IN/OUT: Input to convert size_t *inSize, // IN/OUT: Size of input buffer size_t *outSize, // IN/OUT: Size of output buffer char **bufOut); // IN/OUT: Output buffer HgfsNameStatus CPName_ConvertFromRoot(char const **bufIn, // IN/OUT: Input to convert size_t *inSize, // IN/OUT: Size of input size_t *outSize, // IN/OUT: Size of output buf char **bufOut); // IN/OUT: Output buffer int CPName_GetComponentGeneric(char const *begin, // IN: Beginning of buffer char const *end, // IN: End of buffer char const *illegal, // IN: Illegal characters char const **next); // OUT: Next component int CPName_GetComponent(char const *begin, // IN: Beginning of buffer char const *end, // IN: End of buffer char const **next); // OUT: Next component char const * CPName_Print(char const *in, // IN: Name to print size_t size); // IN: Size of name #endif /* __CP_NAME_H__ */ vmhgfs-only/cpNameLite.h0000444000000000000000000000325612025726745014235 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ /* * cpLiteName.h -- * * Cross-platform "lite" name format used by hgfs. * */ #ifndef __CP_NAME_LITE_H__ #define __CP_NAME_LITE_H__ #if defined(__KERNEL__) && defined(__linux__) # include "driver-config.h" # include #elif defined(_KERNEL) && defined(__FreeBSD__) # include # define strchr(s,c) index(s,c) #else # include #endif #include "vm_basic_types.h" void CPNameLite_ConvertTo(char *bufIn, // IN/OUT: Input to convert size_t inSize, // IN: Size of input buffer char pathSep); // IN: Path separator void CPNameLite_ConvertFrom(char *bufIn, // IN/OUT: Input to convert size_t inSize, // IN: Size of input buffer char pathSep); // IN: Path separator #endif /* __CP_NAME_LITE_H__ */ vmhgfs-only/hgfsProto.h0000444000000000000000000011507112025726745014166 0ustar rootroot/********************************************************* * Copyright (C) 1998 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 * *********************************************************/ /* * hgfsProto.h -- * * Header file for data types and message formats used in the * Host/Guest File System (hgfs) protocol. */ #ifndef _HGFS_PROTO_H_ # define _HGFS_PROTO_H_ #define INCLUDE_ALLOW_USERLEVEL #define INCLUDE_ALLOW_MODULE #define INCLUDE_ALLOW_DISTRIBUTE #include "includeCheck.h" #include "vm_basic_types.h" #include "hgfs.h" /* * Handle used by the server to identify files and searches. Used * by the driver to match server replies with pending requests. */ typedef uint32 HgfsHandle; #define HGFS_INVALID_HANDLE ((HgfsHandle)~((HgfsHandle)0)) /* * Opcodes for server operations. * * Changing the ordering of this enum will break the protocol; new ops * should be added at the end (but before HGFS_OP_MAX). */ typedef enum { HGFS_OP_OPEN, /* Open file */ HGFS_OP_READ, /* Read from file */ HGFS_OP_WRITE, /* Write to file */ HGFS_OP_CLOSE, /* Close file */ HGFS_OP_SEARCH_OPEN, /* Start new search */ HGFS_OP_SEARCH_READ, /* Get next search response */ HGFS_OP_SEARCH_CLOSE, /* End a search */ HGFS_OP_GETATTR, /* Get file attributes */ HGFS_OP_SETATTR, /* Set file attributes */ HGFS_OP_CREATE_DIR, /* Create new directory */ HGFS_OP_DELETE_FILE, /* Delete a file */ HGFS_OP_DELETE_DIR, /* Delete a directory */ HGFS_OP_RENAME, /* Rename a file or directory */ HGFS_OP_QUERY_VOLUME_INFO, /* Query volume information */ /* * The following operations are only available in version 2 of the hgfs * protocol. The corresponding version 1 opcodes above are deprecated. */ HGFS_OP_OPEN_V2, /* Open file */ HGFS_OP_GETATTR_V2, /* Get file attributes */ HGFS_OP_SETATTR_V2, /* Set file attributes */ HGFS_OP_SEARCH_READ_V2, /* Get next search response */ HGFS_OP_CREATE_SYMLINK, /* Create a symlink */ HGFS_OP_SERVER_LOCK_CHANGE, /* Change the oplock on a file */ HGFS_OP_CREATE_DIR_V2, /* Create a directory */ HGFS_OP_DELETE_FILE_V2, /* Delete a file */ HGFS_OP_DELETE_DIR_V2, /* Delete a directory */ HGFS_OP_RENAME_V2, /* Rename a file or directory */ /* * Operations for version 3, deprecating version 2 operations. */ HGFS_OP_OPEN_V3, /* Open file */ HGFS_OP_READ_V3, /* Read from file */ HGFS_OP_WRITE_V3, /* Write to file */ HGFS_OP_CLOSE_V3, /* Close file */ HGFS_OP_SEARCH_OPEN_V3, /* Start new search */ HGFS_OP_SEARCH_READ_V3, /* Start new search */ HGFS_OP_SEARCH_CLOSE_V3, /* End a search */ HGFS_OP_GETATTR_V3, /* Get file attributes */ HGFS_OP_SETATTR_V3, /* Set file attributes */ HGFS_OP_CREATE_DIR_V3, /* Create new directory */ HGFS_OP_DELETE_FILE_V3, /* Delete a file */ HGFS_OP_DELETE_DIR_V3, /* Delete a directory */ HGFS_OP_RENAME_V3, /* Rename a file or directory */ HGFS_OP_QUERY_VOLUME_INFO_V3, /* Query volume information */ HGFS_OP_CREATE_SYMLINK_V3, /* Create a symlink */ HGFS_OP_SERVER_LOCK_CHANGE_V3, /* Change the oplock on a file */ HGFS_OP_MAX, /* Dummy op, must be last in enum */ } HgfsOp; /* HGFS protocol versions. */ #define HGFS_VERSION_OLD (1 << 0) #define HGFS_VERSION_3 (1 << 1) /* XXX: Needs change when VMCI is supported. */ #define HGFS_REQ_PAYLOAD_SIZE_V3(hgfsReq) (sizeof *hgfsReq + sizeof(HgfsRequest)) #define HGFS_REP_PAYLOAD_SIZE_V3(hgfsRep) (sizeof *hgfsRep + sizeof(HgfsReply)) /* XXX: Needs change when VMCI is supported. */ #define HGFS_REQ_GET_PAYLOAD_V3(hgfsReq) ((char *)(hgfsReq) + sizeof(HgfsRequest)) #define HGFS_REP_GET_PAYLOAD_V3(hgfsRep) ((char *)(hgfsRep) + sizeof(HgfsReply)) /* * File types, used in HgfsAttr. We support regular files, * directories, and symlinks. * * Changing the order of this enum will break the protocol; new types * should be added at the end. */ typedef enum { HGFS_FILE_TYPE_REGULAR, HGFS_FILE_TYPE_DIRECTORY, HGFS_FILE_TYPE_SYMLINK, } HgfsFileType; /* * Open flags. * * Changing the order of this enum will break stuff. Do not add any flags to * this enum: it has been frozen and all new flags should be added to * HgfsOpenMode. This was done because HgfsOpenMode could still be converted * to a bitmask (so that it's easier to add flags to) whereas this enum was * already too large. */ typedef enum { // File doesn't exist File exists HGFS_OPEN, // error HGFS_OPEN_EMPTY, // error size = 0 HGFS_OPEN_CREATE, // create HGFS_OPEN_CREATE_SAFE, // create error HGFS_OPEN_CREATE_EMPTY, // create size = 0 } HgfsOpenFlags; /* * Write flags. */ typedef uint8 HgfsWriteFlags; #define HGFS_WRITE_APPEND 1 /* * Permissions bits. * * These are intentionally similar to Unix permissions bits, and we * convert to/from Unix permissions using simple shift operations, so * don't change these or you will break things. */ typedef uint8 HgfsPermissions; #define HGFS_PERM_READ 4 #define HGFS_PERM_WRITE 2 #define HGFS_PERM_EXEC 1 /* * Server-side locking (oplocks and leases). * * The client can ask the server to acquire opportunistic locking/leasing * from the host FS on its behalf. This is communicated as part of an open request. * * HGFS_LOCK_OPPORTUNISTIC means that the client trusts the server * to decide what kind of locking to request from the host FS. * All other values tell the server explicitly the type of lock to * request. * * The server will attempt to acquire the desired lock and will notify the client * which type of lock was acquired as part of the reply to the open request. * Note that HGFS_LOCK_OPPORTUNISTIC should not be specified as the type of * lock acquired by the server, since HGFS_LOCK_OPPORTUNISTIC is not an * actual lock. */ typedef enum { HGFS_LOCK_NONE, HGFS_LOCK_OPPORTUNISTIC, HGFS_LOCK_EXCLUSIVE, HGFS_LOCK_SHARED, } HgfsServerLock; /* * Flags to indicate in a setattr request which fields should be * updated. Deprecated. */ typedef uint8 HgfsAttrChanges; #define HGFS_ATTR_SIZE (1 << 0) #define HGFS_ATTR_CREATE_TIME (1 << 1) #define HGFS_ATTR_ACCESS_TIME (1 << 2) #define HGFS_ATTR_WRITE_TIME (1 << 3) #define HGFS_ATTR_CHANGE_TIME (1 << 4) #define HGFS_ATTR_PERMISSIONS (1 << 5) #define HGFS_ATTR_ACCESS_TIME_SET (1 << 6) #define HGFS_ATTR_WRITE_TIME_SET (1 << 7) /* * Hints to indicate in a getattr or setattr which attributes * are valid for the request. * For setattr only, attributes should be set by host even if * no valid values are specified by the guest. */ typedef uint64 HgfsAttrHint; #define HGFS_ATTR_HINT_SET_ACCESS_TIME (1 << 0) #define HGFS_ATTR_HINT_SET_WRITE_TIME (1 << 1) #define HGFS_ATTR_HINT_USE_FILE_DESC (1 << 2) /* * Hint to determine using a name or a handle to determine * what to delete. */ typedef uint64 HgfsDeleteHint; #define HGFS_DELETE_HINT_USE_FILE_DESC (1 << 0) /* * Hint to determine using a name or a handle to determine * what to renames. */ typedef uint64 HgfsRenameHint; #define HGFS_RENAME_HINT_USE_SRCFILE_DESC (1 << 0) #define HGFS_RENAME_HINT_USE_TARGETFILE_DESC (1 << 1) #define HGFS_RENAME_HINT_NO_REPLACE_EXISTING (1 << 2) #define HGFS_RENAME_HINT_NO_COPY_ALLOWED (1 << 3) /* * File attributes. * * The four time fields below are in Windows NT format, which is in * units of 100ns since Jan 1, 1601, UTC. */ /* * Version 1 attributes. Deprecated. * Version 2 should be using HgfsAttrV2. */ typedef #include "vmware_pack_begin.h" struct HgfsAttr { HgfsFileType type; /* File type */ uint64 size; /* File size (in bytes) */ uint64 creationTime; /* Creation time. Ignored by POSIX */ uint64 accessTime; /* Time of last access */ uint64 writeTime; /* Time of last write */ uint64 attrChangeTime; /* Time file attributess were last * changed. Ignored by Windows */ HgfsPermissions permissions; /* Permissions bits */ } #include "vmware_pack_end.h" HgfsAttr; /* Various flags and Windows attributes. */ typedef uint64 HgfsAttrFlags; #define HGFS_ATTR_HIDDEN (1 << 0) #define HGFS_ATTR_SYSTEM (1 << 1) #define HGFS_ATTR_ARCHIVE (1 << 2) /* * Specifies which open request fields contain * valid values. */ typedef uint64 HgfsOpenValid; #define HGFS_OPEN_VALID_NONE 0 #define HGFS_OPEN_VALID_MODE (1 << 0) #define HGFS_OPEN_VALID_FLAGS (1 << 1) #define HGFS_OPEN_VALID_SPECIAL_PERMS (1 << 2) #define HGFS_OPEN_VALID_OWNER_PERMS (1 << 3) #define HGFS_OPEN_VALID_GROUP_PERMS (1 << 4) #define HGFS_OPEN_VALID_OTHER_PERMS (1 << 5) #define HGFS_OPEN_VALID_FILE_ATTR (1 << 6) #define HGFS_OPEN_VALID_ALLOCATION_SIZE (1 << 7) #define HGFS_OPEN_VALID_DESIRED_ACCESS (1 << 8) #define HGFS_OPEN_VALID_SHARE_ACCESS (1 << 9) #define HGFS_OPEN_VALID_SERVER_LOCK (1 << 10) #define HGFS_OPEN_VALID_FILE_NAME (1 << 11) /* * Specifies which attribute fields contain * valid values. */ typedef uint64 HgfsAttrValid; #define HGFS_ATTR_VALID_NONE 0 #define HGFS_ATTR_VALID_TYPE (1 << 0) #define HGFS_ATTR_VALID_SIZE (1 << 1) #define HGFS_ATTR_VALID_CREATE_TIME (1 << 2) #define HGFS_ATTR_VALID_ACCESS_TIME (1 << 3) #define HGFS_ATTR_VALID_WRITE_TIME (1 << 4) #define HGFS_ATTR_VALID_CHANGE_TIME (1 << 5) #define HGFS_ATTR_VALID_SPECIAL_PERMS (1 << 6) #define HGFS_ATTR_VALID_OWNER_PERMS (1 << 7) #define HGFS_ATTR_VALID_GROUP_PERMS (1 << 8) #define HGFS_ATTR_VALID_OTHER_PERMS (1 << 9) #define HGFS_ATTR_VALID_FLAGS (1 << 10) #define HGFS_ATTR_VALID_ALLOCATION_SIZE (1 << 11) #define HGFS_ATTR_VALID_USERID (1 << 12) #define HGFS_ATTR_VALID_GROUPID (1 << 13) #define HGFS_ATTR_VALID_FILEID (1 << 14) /* * Specifies which create dir request fields contain * valid values. */ typedef uint64 HgfsCreateDirValid; #define HGFS_CREATE_DIR_VALID_NONE 0 #define HGFS_CREATE_DIR_VALID_SPECIAL_PERMS (1 << 0) #define HGFS_CREATE_DIR_VALID_OWNER_PERMS (1 << 1) #define HGFS_CREATE_DIR_VALID_GROUP_PERMS (1 << 2) #define HGFS_CREATE_DIR_VALID_OTHER_PERMS (1 << 3) #define HGFS_CREATE_DIR_VALID_FILE_NAME (1 << 4) /* * Version 2 of HgfsAttr */ typedef #include "vmware_pack_begin.h" struct HgfsAttrV2 { HgfsAttrValid mask; /* A bit mask to determine valid attribute fields */ HgfsFileType type; /* File type */ uint64 size; /* File size (in bytes) */ uint64 creationTime; /* Creation time. Ignored by POSIX */ uint64 accessTime; /* Time of last access */ uint64 writeTime; /* Time of last write */ uint64 attrChangeTime; /* Time file attributes were last * changed. Ignored by Windows */ HgfsPermissions specialPerms; /* Special permissions bits (suid, etc.). * Ignored by Windows */ HgfsPermissions ownerPerms; /* Owner permissions bits */ HgfsPermissions groupPerms; /* Group permissions bits. Ignored by * Windows */ HgfsPermissions otherPerms; /* Other permissions bits. Ignored by * Windows */ HgfsAttrFlags flags; /* Various flags and Windows 'attributes' */ uint64 allocationSize; /* Actual size of file on disk */ uint32 userId; /* User identifier, ignored by Windows */ uint32 groupId; /* group identifier, ignored by Windows */ uint64 hostFileId; /* File Id of the file on host: inode_t on Linux */ uint64 reserved1; /* Reserved for future use */ uint64 reserved2; /* Reserved for future use */ } #include "vmware_pack_end.h" HgfsAttrV2; /* * Cross-platform filename representation * * Cross-platform (CP) names are represented by a string with each * path component separated by NULs, and terminated with a final NUL, * but with no leading path separator. * * For example, the representations of a POSIX and Windows name * are as follows, with "0" meaning NUL. * * Original name Cross-platform name * ----------------------------------------------------- * "/home/bac/temp" -> "home0bac0temp0" * "C:\temp\file.txt" -> "C0temp0file.txt0" * * Note that as in the example above, Windows should strip the colon * off of drive letters as part of the conversion. Aside from that, * all characters in each path component should be left unescaped and * unmodified. Each OS is responsible for escaping any characters that * are not legal in its filenames when converting FROM the CP name * format, and unescaping them when converting TO the CP name format. * * In some requests (OPEN, GETATTR, SETATTR, DELETE, CREATE_DIR) the * CP name is used to represent a particular file, but it is also used * to represent a search pattern for looking up files using * SEARCH_OPEN. * * In the current HGFS server implementation, each request has a minimum packet * size that must be met for it to be considered valid. This minimum is simply * the sizeof the particular request, which includes the solitary byte from the * HgfsFileName struct. For these particular requests, clients add an extra * byte to their payload size, without that byte being present anywhere. * * It isn't clear that this behavior is correct, but the end result is that * neither end malfunctions, as an extra byte gets sent by the client and is * ignored by the server. Unfortunately, it cannot be easily fixed. The * server's minimum packet size can be changed, but the client should continue * to send an extra byte, otherwise older servers with a slightly longer * minimum packet size may consider the new client's packets to be too short. * * UTF-8 representation * -------------------- * XXX: It is expected that file names in the HGFS protocol will be a valid UTF-8 * encoding. * See RFC 3629 (http://tools.ietf.org/html/rfc3629) * * Unicode Format * -------------- * HGFS protocol requests that contain file names as in the structure below, * should contain unicode normal form C (precomposed see explanation below) * characters therefore hosts such as Mac OS X which * use HFS+ and unicode form D should convert names before * processing or sending HGFS requests. * * Precomposed (normal form C) versus Decomposed (normal form D) * ------------------------------------------------------------- * Certain Unicode characters can be encoded in more than one way. * For example, an (A acute) can be encoded either precomposed, * as U+00C1 (LATIN CAPITAL LETTER A WITH ACUTE), or decomposed, * as U+0041 U+0301 (LATIN CAPITAL LETTER A followed by a COMBINING ACUTE ACCENT). * Precomposed characters are more common in the Windows world, * whereas decomposed characters are more common on the Mac. * * See UAX 15 (http://unicode.org/reports/tr15/) */ typedef #include "vmware_pack_begin.h" struct HgfsFileName { uint32 length; /* Does NOT include terminating NUL */ char name[1]; } #include "vmware_pack_end.h" HgfsFileName; /* * Case-sensitiviy flags are only used when any lookup is * involved on the server side. */ typedef enum { HGFS_FILE_NAME_DEFAULT_CASE, HGFS_FILE_NAME_CASE_SENSITIVE, HGFS_FILE_NAME_CASE_INSENSITIVE, } HgfsCaseType; /* * HgfsFileNameV3 - new header to incorporate case-sensitivity flags along with * Hgfs file handle. */ typedef #include "vmware_pack_begin.h" struct HgfsFileNameV3 { uint32 length; /* Does NOT include terminating NUL */ uint32 flags; /* Flags described below. */ HgfsCaseType caseType; /* Case-sensitivity type. */ HgfsHandle fid; char name[1]; } #include "vmware_pack_end.h" HgfsFileNameV3; /* * HgfsFileNameV3 flags. Case-sensitiviy flags are only used when any lookup is * involved on the server side. */ #define HGFS_FILE_NAME_USE_FILE_DESC (1 << 0) /* Case type ignored if set. */ /* * Request/reply structs. These are the first members of all * operation request and reply messages, respectively. */ typedef #include "vmware_pack_begin.h" struct HgfsRequest { HgfsHandle id; /* Opaque request ID used by the requestor */ HgfsOp op; } #include "vmware_pack_end.h" HgfsRequest; typedef #include "vmware_pack_begin.h" struct HgfsReply { HgfsHandle id; /* Opaque request ID used by the requestor */ HgfsStatus status; } #include "vmware_pack_end.h" HgfsReply; /* * Messages for our file operations. */ /* Deprecated */ typedef #include "vmware_pack_begin.h" struct HgfsRequestOpen { HgfsRequest header; HgfsOpenMode mode; /* Which type of access is requested */ HgfsOpenFlags flags; /* Which flags to open the file with */ HgfsPermissions permissions; /* Which permissions to *create* a new file with */ HgfsFileName fileName; } #include "vmware_pack_end.h" HgfsRequestOpen; /* Version 2 of HgfsRequestOpen */ typedef #include "vmware_pack_begin.h" struct HgfsRequestOpenV2 { HgfsRequest header; HgfsOpenValid mask; /* Bitmask that specified which fields are valid. */ HgfsOpenMode mode; /* Which type of access requested. See desiredAccess */ HgfsOpenFlags flags; /* Which flags to open the file with */ HgfsPermissions specialPerms; /* Desired 'special' permissions for file creation */ HgfsPermissions ownerPerms; /* Desired 'owner' permissions for file creation */ HgfsPermissions groupPerms; /* Desired 'group' permissions for file creation */ HgfsPermissions otherPerms; /* Desired 'other' permissions for file creation */ HgfsAttrFlags attr; /* Attributes, if any, for file creation */ uint64 allocationSize; /* How much space to pre-allocate during creation */ uint32 desiredAccess; /* Extended support for windows access modes */ uint32 shareAccess; /* Windows only, share access modes */ HgfsServerLock desiredLock; /* The type of lock desired by the client */ uint64 reserved1; /* Reserved for future use */ uint64 reserved2; /* Reserved for future use */ HgfsFileName fileName; } #include "vmware_pack_end.h" HgfsRequestOpenV2; /* Version 3 of HgfsRequestOpen */ typedef #include "vmware_pack_begin.h" struct HgfsRequestOpenV3 { HgfsOpenValid mask; /* Bitmask that specified which fields are valid. */ HgfsOpenMode mode; /* Which type of access requested. See desiredAccess */ HgfsOpenFlags flags; /* Which flags to open the file with */ HgfsPermissions specialPerms; /* Desired 'special' permissions for file creation */ HgfsPermissions ownerPerms; /* Desired 'owner' permissions for file creation */ HgfsPermissions groupPerms; /* Desired 'group' permissions for file creation */ HgfsPermissions otherPerms; /* Desired 'other' permissions for file creation */ HgfsAttrFlags attr; /* Attributes, if any, for file creation */ uint64 allocationSize; /* How much space to pre-allocate during creation */ uint32 desiredAccess; /* Extended support for windows access modes */ uint32 shareAccess; /* Windows only, share access modes */ HgfsServerLock desiredLock; /* The type of lock desired by the client */ uint64 reserved1; /* Reserved for future use */ uint64 reserved2; /* Reserved for future use */ HgfsFileNameV3 fileName; } #include "vmware_pack_end.h" HgfsRequestOpenV3; /* Deprecated */ typedef #include "vmware_pack_begin.h" struct HgfsReplyOpen { HgfsReply header; HgfsHandle file; /* Opaque file ID used by the server */ } #include "vmware_pack_end.h" HgfsReplyOpen; /* Version 2 of HgfsReplyOpen */ typedef #include "vmware_pack_begin.h" struct HgfsReplyOpenV2 { HgfsReply header; HgfsHandle file; /* Opaque file ID used by the server */ HgfsServerLock acquiredLock; /* The type of lock acquired by the server */ } #include "vmware_pack_end.h" HgfsReplyOpenV2; /* Version 3 of HgfsReplyOpen */ typedef #include "vmware_pack_begin.h" struct HgfsReplyOpenV3 { HgfsHandle file; /* Opaque file ID used by the server */ HgfsServerLock acquiredLock; /* The type of lock acquired by the server */ uint64 reserved; /* Reserved for future use */ } #include "vmware_pack_end.h" HgfsReplyOpenV3; /* Deprecated */ typedef #include "vmware_pack_begin.h" struct HgfsRequestRead { HgfsRequest header; HgfsHandle file; /* Opaque file ID used by the server */ uint64 offset; uint32 requiredSize; } #include "vmware_pack_end.h" HgfsRequestRead; /* Deprecated */ typedef #include "vmware_pack_begin.h" struct HgfsReplyRead { HgfsReply header; uint32 actualSize; char payload[1]; } #include "vmware_pack_end.h" HgfsReplyRead; /* * Version 3 of HgfsRequestRead. * Server must support HGFS_LARGE_PACKET_MAX to implement this op. */ typedef #include "vmware_pack_begin.h" struct HgfsRequestReadV3 { HgfsHandle file; /* Opaque file ID used by the server */ uint64 offset; uint32 requiredSize; uint64 reserved; /* Reserved for future use */ } #include "vmware_pack_end.h" HgfsRequestReadV3; typedef #include "vmware_pack_begin.h" struct HgfsReplyReadV3 { uint32 actualSize; uint64 reserved; /* Reserved for future use */ char payload[1]; } #include "vmware_pack_end.h" HgfsReplyReadV3; /* Deprecated */ typedef #include "vmware_pack_begin.h" struct HgfsRequestWrite { HgfsRequest header; HgfsHandle file; /* Opaque file ID used by the server */ HgfsWriteFlags flags; uint64 offset; uint32 requiredSize; char payload[1]; } #include "vmware_pack_end.h" HgfsRequestWrite; /* Deprecated */ typedef #include "vmware_pack_begin.h" struct HgfsReplyWrite { HgfsReply header; uint32 actualSize; } #include "vmware_pack_end.h" HgfsReplyWrite; /* * Version 3 of HgfsRequestWrite. * Server must support HGFS_LARGE_PACKET_MAX to implement this op. */ typedef #include "vmware_pack_begin.h" struct HgfsRequestWriteV3 { HgfsHandle file; /* Opaque file ID used by the server */ HgfsWriteFlags flags; uint64 offset; uint32 requiredSize; uint64 reserved; /* Reserved for future use */ char payload[1]; } #include "vmware_pack_end.h" HgfsRequestWriteV3; typedef #include "vmware_pack_begin.h" struct HgfsReplyWriteV3 { uint32 actualSize; uint64 reserved; /* Reserved for future use */ } #include "vmware_pack_end.h" HgfsReplyWriteV3; /* Deprecated */ typedef #include "vmware_pack_begin.h" struct HgfsRequestClose { HgfsRequest header; HgfsHandle file; /* Opaque file ID used by the server */ } #include "vmware_pack_end.h" HgfsRequestClose; /* Deprecated */ typedef #include "vmware_pack_begin.h" struct HgfsReplyClose { HgfsReply header; } #include "vmware_pack_end.h" HgfsReplyClose; typedef #include "vmware_pack_begin.h" struct HgfsRequestCloseV3 { HgfsHandle file; /* Opaque file ID used by the server */ uint64 reserved; /* Reserved for future use */ } #include "vmware_pack_end.h" HgfsRequestCloseV3; typedef #include "vmware_pack_begin.h" struct HgfsReplyCloseV3 { uint64 reserved; } #include "vmware_pack_end.h" HgfsReplyCloseV3; /* Deprecated */ typedef #include "vmware_pack_begin.h" struct HgfsRequestSearchOpen { HgfsRequest header; HgfsFileName dirName; } #include "vmware_pack_end.h" HgfsRequestSearchOpen; typedef #include "vmware_pack_begin.h" struct HgfsRequestSearchOpenV3 { uint64 reserved; /* Reserved for future use */ HgfsFileNameV3 dirName; } #include "vmware_pack_end.h" HgfsRequestSearchOpenV3; /* Deprecated */ typedef #include "vmware_pack_begin.h" struct HgfsReplySearchOpen { HgfsReply header; HgfsHandle search; /* Opaque search ID used by the server */ } #include "vmware_pack_end.h" HgfsReplySearchOpen; typedef #include "vmware_pack_begin.h" struct HgfsReplySearchOpenV3 { HgfsHandle search; /* Opaque search ID used by the server */ uint64 reserved; /* Reserved for future use */ } #include "vmware_pack_end.h" HgfsReplySearchOpenV3; /* Deprecated */ typedef #include "vmware_pack_begin.h" struct HgfsRequestSearchRead { HgfsRequest header; HgfsHandle search; /* Opaque search ID used by the server */ uint32 offset; /* The first result is offset 0 */ } #include "vmware_pack_end.h" HgfsRequestSearchRead; /* Version 2 of HgfsRequestSearchRead */ typedef #include "vmware_pack_begin.h" struct HgfsRequestSearchReadV2 { HgfsRequest header; HgfsHandle search; /* Opaque search ID used by the server */ uint32 offset; /* The first result is offset 0 */ } #include "vmware_pack_end.h" HgfsRequestSearchReadV2; typedef #include "vmware_pack_begin.h" struct HgfsRequestSearchReadV3 { HgfsHandle search; /* Opaque search ID used by the server */ uint32 offset; /* The first result is offset 0 */ uint32 flags; /* Reserved for reading multiple directory entries. */ uint64 reserved; /* Reserved for future use */ } #include "vmware_pack_end.h" HgfsRequestSearchReadV3; /* Deprecated */ typedef #include "vmware_pack_begin.h" struct HgfsReplySearchRead { HgfsReply header; HgfsAttr attr; HgfsFileName fileName; /* fileName.length = 0 means "no entry at this offset" */ } #include "vmware_pack_end.h" HgfsReplySearchRead; /* Version 2 of HgfsReplySearchRead */ typedef #include "vmware_pack_begin.h" struct HgfsReplySearchReadV2 { HgfsReply header; HgfsAttrV2 attr; /* * fileName.length = 0 means "no entry at this offset" * If the file is a symlink (as specified in attr) * this name is the name of the symlink, not the target. */ HgfsFileName fileName; } #include "vmware_pack_end.h" HgfsReplySearchReadV2; /* Directory entry structure. */ typedef struct HgfsDirEntry { uint32 nextEntry; HgfsAttrV2 attr; /* * fileName.length = 0 means "no entry at this offset" * If the file is a symlink (as specified in attr) * this name is the name of the symlink, not the target. */ HgfsFileNameV3 fileName; } HgfsDirEntry; typedef #include "vmware_pack_begin.h" struct HgfsReplySearchReadV3 { uint64 count; /* Number of directory entries. */ uint64 reserved; /* Reserved for future use. */ char payload[1]; /* Directory entries. */ } #include "vmware_pack_end.h" HgfsReplySearchReadV3; /* Deprecated */ typedef #include "vmware_pack_begin.h" struct HgfsRequestSearchClose { HgfsRequest header; HgfsHandle search; /* Opaque search ID used by the server */ } #include "vmware_pack_end.h" HgfsRequestSearchClose; /* Deprecated */ typedef #include "vmware_pack_begin.h" struct HgfsReplySearchClose { HgfsReply header; } #include "vmware_pack_end.h" HgfsReplySearchClose; typedef #include "vmware_pack_begin.h" struct HgfsRequestSearchCloseV3 { HgfsHandle search; /* Opaque search ID used by the server */ uint64 reserved; /* Reserved for future use */ } #include "vmware_pack_end.h" HgfsRequestSearchCloseV3; typedef #include "vmware_pack_begin.h" struct HgfsReplySearchCloseV3 { uint64 reserved; /* Reserved for future use */ } #include "vmware_pack_end.h" HgfsReplySearchCloseV3; /* Deprecated */ typedef #include "vmware_pack_begin.h" struct HgfsRequestGetattr { HgfsRequest header; HgfsFileName fileName; } #include "vmware_pack_end.h" HgfsRequestGetattr; /* Version 2 of HgfsRequestGetattr */ typedef #include "vmware_pack_begin.h" struct HgfsRequestGetattrV2 { HgfsRequest header; HgfsAttrHint hints; /* Flags for file handle valid. */ HgfsHandle file; /* Opaque file ID used by the server. */ HgfsFileName fileName; /* Filename used when file handle invalid. */ } #include "vmware_pack_end.h" HgfsRequestGetattrV2; typedef #include "vmware_pack_begin.h" struct HgfsRequestGetattrV3 { HgfsAttrHint hints; /* Flags for file handle valid. */ uint64 reserved; /* Reserved for future use */ HgfsFileNameV3 fileName; /* Filename used when file handle invalid. */ } #include "vmware_pack_end.h" HgfsRequestGetattrV3; /* Deprecated */ typedef #include "vmware_pack_begin.h" struct HgfsReplyGetattr { HgfsReply header; HgfsAttr attr; } #include "vmware_pack_end.h" HgfsReplyGetattr; /* Version 2 of HgfsReplyGetattr */ typedef #include "vmware_pack_begin.h" struct HgfsReplyGetattrV2 { HgfsReply header; HgfsAttrV2 attr; /* * If the file is a symlink, as specified in attr.type, then this is * the target for the symlink. If the file is not a symlink, this should * be ignored. * * This filename is in "CPNameLite" format. See CPNameLite.c for details. */ HgfsFileName symlinkTarget; } #include "vmware_pack_end.h" HgfsReplyGetattrV2; typedef #include "vmware_pack_begin.h" struct HgfsReplyGetattrV3 { HgfsAttrV2 attr; /* * If the file is a symlink, as specified in attr.type, then this is * the target for the symlink. If the file is not a symlink, this should * be ignored. * * This filename is in "CPNameLite" format. See CPNameLite.c for details. */ uint64 reserved; /* Reserved for future use */ HgfsFileNameV3 symlinkTarget; } #include "vmware_pack_end.h" HgfsReplyGetattrV3; /* Deprecated */ typedef #include "vmware_pack_begin.h" struct HgfsRequestSetattr { HgfsRequest header; HgfsAttrChanges update; /* Which fields need to be updated */ HgfsAttr attr; HgfsFileName fileName; } #include "vmware_pack_end.h" HgfsRequestSetattr; /* Version 2 of HgfsRequestSetattr */ typedef #include "vmware_pack_begin.h" struct HgfsRequestSetattrV2 { HgfsRequest header; HgfsAttrHint hints; HgfsAttrV2 attr; HgfsHandle file; /* Opaque file ID used by the server. */ HgfsFileName fileName; /* Filename used when file handle invalid. */ } #include "vmware_pack_end.h" HgfsRequestSetattrV2; typedef #include "vmware_pack_begin.h" struct HgfsRequestSetattrV3 { HgfsAttrHint hints; HgfsAttrV2 attr; uint64 reserved; /* Reserved for future use */ HgfsFileNameV3 fileName; /* Filename used when file handle invalid. */ } #include "vmware_pack_end.h" HgfsRequestSetattrV3; /* Deprecated */ typedef #include "vmware_pack_begin.h" struct HgfsReplySetattr { HgfsReply header; } #include "vmware_pack_end.h" HgfsReplySetattr; /* Version 2 of HgfsReplySetattr */ typedef #include "vmware_pack_begin.h" struct HgfsReplySetattrV2 { HgfsReply header; } #include "vmware_pack_end.h" HgfsReplySetattrV2; typedef #include "vmware_pack_begin.h" struct HgfsReplySetattrV3 { uint64 reserved; /* Reserved for future use */ } #include "vmware_pack_end.h" HgfsReplySetattrV3; /* Deprecated */ typedef #include "vmware_pack_begin.h" struct HgfsRequestCreateDir { HgfsRequest header; HgfsPermissions permissions; HgfsFileName fileName; } #include "vmware_pack_end.h" HgfsRequestCreateDir; /* Version 2 of HgfsRequestCreateDir */ typedef #include "vmware_pack_begin.h" struct HgfsRequestCreateDirV2 { HgfsRequest header; HgfsCreateDirValid mask; HgfsPermissions specialPerms; HgfsPermissions ownerPerms; HgfsPermissions groupPerms; HgfsPermissions otherPerms; HgfsFileName fileName; } #include "vmware_pack_end.h" HgfsRequestCreateDirV2; /* Version 3 of HgfsRequestCreateDir */ typedef #include "vmware_pack_begin.h" struct HgfsRequestCreateDirV3 { HgfsCreateDirValid mask; HgfsPermissions specialPerms; HgfsPermissions ownerPerms; HgfsPermissions groupPerms; HgfsPermissions otherPerms; uint64 reserved; /* Reserved for future use */ HgfsFileNameV3 fileName; } #include "vmware_pack_end.h" HgfsRequestCreateDirV3; /* Deprecated */ typedef #include "vmware_pack_begin.h" struct HgfsReplyCreateDir { HgfsReply header; } #include "vmware_pack_end.h" HgfsReplyCreateDir; /* Version 2 of HgfsReplyCreateDir */ typedef #include "vmware_pack_begin.h" struct HgfsReplyCreateDirV2 { HgfsReply header; } #include "vmware_pack_end.h" HgfsReplyCreateDirV2; /* Version 3 of HgfsReplyCreateDir */ typedef #include "vmware_pack_begin.h" struct HgfsReplyCreateDirV3 { uint64 reserved; /* Reserved for future use */ } #include "vmware_pack_end.h" HgfsReplyCreateDirV3; /* Deprecated */ typedef #include "vmware_pack_begin.h" struct HgfsRequestDelete { HgfsRequest header; HgfsFileName fileName; } #include "vmware_pack_end.h" HgfsRequestDelete; /* Version 2 of HgfsRequestDelete */ typedef #include "vmware_pack_begin.h" struct HgfsRequestDeleteV2 { HgfsRequest header; HgfsDeleteHint hints; HgfsHandle file; /* Opaque file ID used by the server. */ HgfsFileName fileName; /* Name used if the file is HGFS_HANDLE_INVALID */ } #include "vmware_pack_end.h" HgfsRequestDeleteV2; /* Version 3 of HgfsRequestDelete */ typedef #include "vmware_pack_begin.h" struct HgfsRequestDeleteV3 { HgfsDeleteHint hints; uint64 reserved; /* Reserved for future use */ HgfsFileNameV3 fileName; /* Name used if the file is HGFS_HANDLE_INVALID */ } #include "vmware_pack_end.h" HgfsRequestDeleteV3; /* Deprecated */ typedef #include "vmware_pack_begin.h" struct HgfsReplyDelete { HgfsReply header; } #include "vmware_pack_end.h" HgfsReplyDelete; /* Version 2 of HgfsReplyDelete */ typedef #include "vmware_pack_begin.h" struct HgfsReplyDeleteV2 { HgfsReply header; } #include "vmware_pack_end.h" HgfsReplyDeleteV2; /* Version 2 of HgfsReplyDelete */ typedef #include "vmware_pack_begin.h" struct HgfsReplyDeleteV3 { uint64 reserved; /* Reserved for future use */ } #include "vmware_pack_end.h" HgfsReplyDeleteV3; /* * The size of the HgfsFileName struct is variable depending on the * length of the name, so you can't use request->newName to get the * actual address of the new name, because where it starts is * dependant on how long the oldName is. To get the address of * newName, use this: * * &oldName + sizeof(HgfsFileName) + oldName.length */ typedef #include "vmware_pack_begin.h" struct HgfsRequestRename { HgfsRequest header; HgfsFileName oldName; HgfsFileName newName; } #include "vmware_pack_end.h" HgfsRequestRename; typedef #include "vmware_pack_begin.h" struct HgfsReplyRename { HgfsReply header; } #include "vmware_pack_end.h" HgfsReplyRename; typedef #include "vmware_pack_begin.h" struct HgfsRequestRenameV2 { HgfsRequest header; HgfsRenameHint hints; HgfsHandle srcFile; /* Opaque file ID to "old name" used by the server. */ HgfsHandle targetFile; /* Opaque file ID to "old name" used by the server. */ HgfsFileName oldName; HgfsFileName newName; } #include "vmware_pack_end.h" HgfsRequestRenameV2; typedef #include "vmware_pack_begin.h" struct HgfsReplyRenameV2 { HgfsReply header; } #include "vmware_pack_end.h" HgfsReplyRenameV2; /* HgfsRequestRename and HgfsReplyRename for v3. */ typedef #include "vmware_pack_begin.h" struct HgfsRequestRenameV3 { HgfsRenameHint hints; uint64 reserved; /* Reserved for future use */ HgfsFileNameV3 oldName; HgfsFileNameV3 newName; } #include "vmware_pack_end.h" HgfsRequestRenameV3; typedef #include "vmware_pack_begin.h" struct HgfsReplyRenameV3 { uint64 reserved; /* Reserved for future use */ } #include "vmware_pack_end.h" HgfsReplyRenameV3; typedef #include "vmware_pack_begin.h" struct HgfsRequestQueryVolume { HgfsRequest header; HgfsFileName fileName; } #include "vmware_pack_end.h" HgfsRequestQueryVolume; typedef #include "vmware_pack_begin.h" struct HgfsReplyQueryVolume { HgfsReply header; uint64 freeBytes; uint64 totalBytes; } #include "vmware_pack_end.h" HgfsReplyQueryVolume; /* HgfsRequestQueryVolume and HgfsReplyQueryVolume for v3. */ typedef #include "vmware_pack_begin.h" struct HgfsRequestQueryVolumeV3 { uint64 reserved; /* Reserved for future use */ HgfsFileNameV3 fileName; } #include "vmware_pack_end.h" HgfsRequestQueryVolumeV3; typedef #include "vmware_pack_begin.h" struct HgfsReplyQueryVolumeV3 { uint64 freeBytes; uint64 totalBytes; uint64 reserved; /* Reserved for future use */ } #include "vmware_pack_end.h" HgfsReplyQueryVolumeV3; /* New operations for Version 2 */ typedef #include "vmware_pack_begin.h" struct HgfsRequestServerLockChange { HgfsRequest header; HgfsHandle file; HgfsServerLock newServerLock; } #include "vmware_pack_end.h" HgfsRequestServerLockChange; typedef #include "vmware_pack_begin.h" struct HgfsReplyServerLockChange { HgfsReply header; HgfsServerLock serverLock; } #include "vmware_pack_end.h" HgfsReplyServerLockChange; typedef #include "vmware_pack_begin.h" struct HgfsRequestSymlinkCreate { HgfsRequest header; HgfsFileName symlinkName; /* This filename is in "CPNameLite" format. See CPNameLite.c for details. */ HgfsFileName targetName; } #include "vmware_pack_end.h" HgfsRequestSymlinkCreate; typedef #include "vmware_pack_begin.h" struct HgfsReplySymlinkCreate { HgfsReply header; } #include "vmware_pack_end.h" HgfsReplySymlinkCreate; /* HgfsRequestSymlinkCreate and HgfsReplySymlinkCreate for v3. */ typedef #include "vmware_pack_begin.h" struct HgfsRequestSymlinkCreateV3 { uint64 reserved; /* Reserved for future use */ HgfsFileNameV3 symlinkName; /* This filename is in "CPNameLite" format. See CPNameLite.c for details. */ HgfsFileNameV3 targetName; } #include "vmware_pack_end.h" HgfsRequestSymlinkCreateV3; typedef #include "vmware_pack_begin.h" struct HgfsReplySymlinkCreateV3 { uint64 reserved; /* Reserved for future use */ } #include "vmware_pack_end.h" HgfsReplySymlinkCreateV3; #endif /* _HGFS_PROTO_H_ */ vmhgfs-only/staticEscape.c0000444000000000000000000002116712025726745014620 0ustar rootroot/********************************************************* * Copyright (C) 1998 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 * *********************************************************/ /* * staticEscape.c -- * * Buffer escaping, stolen from hpreg's buffer escaping * in lib/string, but modified to use bit vectors instead * of arrays, and static buffers instead of dynbufs. [bac] * */ #if defined(sun) # include #elif defined(__FreeBSD__) # if defined(_KERNEL) # include # define memmove(dst0, src0, len) bcopy(src0, dst0, len) # else # include # endif #endif #include "staticEscape.h" #include "vm_assert.h" /* * Table to use to quickly convert an ASCII hexadecimal digit character into a * decimal number. If the input is not an hexadecimal digit character, the * output is -1 --hpreg */ static int const Hex2Dec[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, }; /* * Table to use to quickly convert a decimal number into an ASCII hexadecimal * digit character --hpreg */ static char const Dec2Hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', }; /* *----------------------------------------------------------------------------- * * StaticEscape_Do -- * * Escape a buffer. Expects sizeBufOut to account for the NUL terminator. * * You can calculate the required size of the output buffer as follows: * sizeBufOut >= (((sizeIn - # of chars to be escaped) * sizeof *sizeIn) + * (sizeof escSeq * # of chars to be escaped) + * sizeof '\0') * * Or, in English, "the number of non-escaped characters times each * non-escaped character's size, plus the number of escaped characters times * each escaped character's size, plus the size of the NUL terminator" (not * that this is very useful, since most callers won't take the time to * determine the number of characters to be escaped up front). * * Note that this function assumes one to one mapping between characters * and bytes. This works for any ASCII-transparent encodings (such as UTF8). * * XXX: An interface with an input size in characters and an output size in * bytes is broken (especially when the the return value is in bytes, but * _without_ the NUL terminator). We do it to maintain consistency with * the StaticEscapeW interface, where the distinction between characters * and bytes is actually important. * * Results: * On success, the size (excluding the NUL terminator) of the * escaped, NUL terminated buffer. * On failure (bufOut not big enough to hold result), negative value. * * Side effects: * None * *----------------------------------------------------------------------------- */ int StaticEscape_Do(char escByte, // IN: The escape character EscBitVector const *bytesToEsc, // IN: Chars we must escape void const *bufIn, // IN: Input buffer uint32 sizeIn, // IN: Size of bufIn (chars) uint32 sizeBufOut, // IN: Size of bufOut (bytes) char *bufOut) // OUT: Output buffer { char const *buf; unsigned int startUnescaped; unsigned int index; char escSeq[3]; int copySize; int outPos; /* Make sure we won't obviously overflow the bufOut [bac] */ if (sizeIn > sizeBufOut) { return -1; } ASSERT(bytesToEsc); /* Unsigned does matter --hpreg */ ASSERT(EscBitVector_Test(bytesToEsc, (unsigned char)escByte)); buf = (char const *)bufIn; ASSERT(buf); escSeq[0] = escByte; startUnescaped = 0; outPos = 0; for (index = 0; index < sizeIn; index++) { /* Unsigned does matter --hpreg */ unsigned char ubyte; ubyte = buf[index]; if (EscBitVector_Test(bytesToEsc, ubyte)) { /* We must escape that byte --hpreg */ escSeq[1] = Dec2Hex[ubyte >> 4]; escSeq[2] = Dec2Hex[ubyte & 0xF]; copySize = index - startUnescaped; if (outPos + copySize + sizeof(escSeq) > sizeBufOut) { /* * Make sure that both the first chunk and the * escape sequence will fit in the bufOut. [bac] */ return -1; } memcpy(&bufOut[outPos], &buf[startUnescaped], copySize); outPos += copySize; copySize = sizeof(escSeq); memcpy(&bufOut[outPos], escSeq, copySize); outPos += copySize; startUnescaped = index + 1; } } copySize = index - startUnescaped; if (outPos + copySize + 1 > sizeBufOut) { /* * Make sure the terminating NUL will fit too, so we don't have * to check again below. [bac] */ return -1; } memcpy(&bufOut[outPos], &buf[startUnescaped], copySize); outPos += copySize; memcpy(&bufOut[outPos], "", 1); return outPos; /* Size of the output buf, not counting NUL terminator */ } /* *----------------------------------------------------------------------------- * * StaticEscape_Undo -- * * Unescape a buffer --hpreg * * The unescaping is done in place in the input buffer, and * thus can not fail. * * Results: * The size (excluding the NUL terminator) of the unescaped, NUL * terminated buffer. * * Side effects: * None * *----------------------------------------------------------------------------- */ int StaticEscape_Undo(char escByte, // IN void *bufIn, // IN uint32 sizeIn) // IN { char *buf; unsigned int state; unsigned int startUnescaped; unsigned int index; int outPos; int copySize; int h = 0; /* Compiler warning --hpreg */ int l; buf = (char *)bufIn; ASSERT(buf); outPos = 0; startUnescaped = 0; state = 0; for (index = 0; index < sizeIn; index++) { /* Unsigned does matter --hpreg */ unsigned char ubyte; ubyte = buf[index]; switch (state) { case 0: /* Found --hpreg */ if (ubyte == escByte) { state = 1; } break; case 1: /* Found --hpreg */ h = Hex2Dec[ubyte]; state = h >= 0 ? 2 : 0; break; case 2: /* Found --hpreg */ l = Hex2Dec[ubyte]; if (l >= 0) { char escaped; escaped = h << 4 | l; copySize = index - 2 - startUnescaped; memmove(&buf[outPos], &buf[startUnescaped], copySize); outPos += copySize; memcpy(&buf[outPos], &escaped, 1); outPos++; startUnescaped = index + 1; } state = 0; break; default: NOT_IMPLEMENTED(); break; } } /* Last unescaped chunk (if any) --hpreg */ copySize = index - startUnescaped; memmove(&buf[outPos], &buf[startUnescaped], copySize); outPos += copySize; memcpy(&buf[outPos], "", 1); return outPos; } vmhgfs-only/cpNameInt.h0000444000000000000000000000340612025726745014067 0ustar rootroot/********************************************************* * Copyright (C) 1998 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 * *********************************************************/ /* * cpNameInt.h -- * * Cross-platform name format used by hgfs. * */ #ifndef __CP_NAME_INT_H__ #define __CP_NAME_INT_H__ #include "vm_basic_types.h" /* * Used by CPName_ConvertFrom */ int CPNameConvertFrom(char const **bufIn, // IN/OUT: Input to convert size_t *inSize, // IN/OUT: Size of input size_t *outSize, // IN/OUT: Size of output buffer char **bufOut, // IN/OUT: Output buffer char pathSep); // IN: Path separator character /* * Common code for CPName_ConvertTo */ int CPNameConvertTo(char const *nameIn, // IN: Buf to convert size_t bufOutSize, // IN: Size of the output buffer char *bufOut, // OUT: Output buffer char pathSep, // IN: path separator to use char *ignores); // IN: chars to not transfer to output #endif /* __CP_NAME_INT_H__ */ vmhgfs-only/escBitvector.h0000444000000000000000000000737012025726745014651 0ustar rootroot/********************************************************* * Copyright (C) 1998 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 * *********************************************************/ #ifndef _ESC_BITVECTOR_H_ #define _ESC_BITVECTOR_H_ #define INCLUDE_ALLOW_USERLEVEL #define INCLUDE_ALLOW_MODULE #define INCLUDE_ALLOW_DISTRIBUTE // XXX is this true? #include "includeCheck.h" #ifdef __KERNEL__ #include "driver-config.h" #include /* Don't include these if compiling for the Solaris or Apple kernels. */ #elif !defined(_KERNEL) && !defined(KERNEL) #include #include #endif #if defined(_KERNEL) && defined(__FreeBSD__) # include #elif defined(KERNEL) && defined(__APPLE__) # include #endif #include "vm_assert.h" #define ESC_BITVECTOR_INDEX(_x) ((_x)>>5) #define ESC_BITVECTOR_MASK(_x) (1<<((_x)&31)) #define ESC_BITVECTOR_SIZE 256 // hardwired size of the bitvector /* *---------------------------------------------------------------------- * * EscBitVector -- * * Taken from bitvector.h, but hard wired for use with the Escape * routines, which always need a bitvector of 256 bits, are never * used in the monitor, and need to work in the linux kernel. [bac] * * *---------------------------------------------------------------------- */ typedef struct EscBitVector { uint32 vector[ESC_BITVECTOR_SIZE/32]; } EscBitVector; /* *---------------------------------------------------------------------- * * EscBitVector_Init -- * * Clear all the bits in this vector. * * Results: * All bits are cleared * *---------------------------------------------------------------------- */ static INLINE void EscBitVector_Init(EscBitVector *bv) { memset(bv, 0, sizeof(EscBitVector)); } /* *---------------------------------------------------------------------- * * EscBitVector_Set, EscBitVector_Clear, EscBitVector_Test -- * * basic operations * * Results: * insertion/deletion/presence to/from/in the set * * EscBitVector_Test returns non-zero if present, 0 otherwise * * *---------------------------------------------------------------------- */ static INLINE void EscBitVector_Set(EscBitVector *bv,int n) { ASSERT(n>=0 && nvector[0]) :"Ir" (n)); #else bv->vector[ESC_BITVECTOR_INDEX(n)] |= ESC_BITVECTOR_MASK(n); #endif } static INLINE void EscBitVector_Clear(EscBitVector *bv,int n) { ASSERT(n>=0 && nvector[0]) :"Ir" (n)); #else bv->vector[ESC_BITVECTOR_INDEX(n)] &= ~ESC_BITVECTOR_MASK(n); #endif } static INLINE int EscBitVector_Test(EscBitVector const *bv, int n) { ASSERT(n>=0 && nvector[0]),"Ir" (n)); return tmp; } #else return ((bv->vector[ESC_BITVECTOR_INDEX(n)] & ESC_BITVECTOR_MASK(n)) != 0); #endif } #endif /* _ESC_BITVECTOR_H_ */ vmhgfs-only/hgfsUtil.c0000444000000000000000000001524612025726745013776 0ustar rootroot/********************************************************* * Copyright (C) 1998 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 * *********************************************************/ /* * hgfsUtil.c -- * * Utility routines used by both HGFS servers and clients, such as * conversion routines between Unix time and Windows NT time. * The former is in units of seconds since midnight 1/1/1970, while the * latter is in units of 100 nanoseconds since midnight 1/1/1601. */ /* * hgfsUtil.h must be included before vm_basic_asm.h, as hgfsUtil.h * includes kernel headers on Linux. That is, vmware.h must come after * hgfsUtil.h. */ #include "hgfsUtil.h" #include "vmware.h" #include "vm_basic_asm.h" #ifndef _WIN32 /* * NT time of the Unix epoch: * midnight January 1, 1970 UTC */ #define UNIX_EPOCH ((((uint64)369 * 365) + 89) * 24 * 3600 * 10000000) /* * NT time of the Unix 32 bit signed time_t wraparound: * 03:14:07 January 19, 2038 UTC */ #define UNIX_S32_MAX (UNIX_EPOCH + (uint64)0x80000000 * 10000000) /* *----------------------------------------------------------------------------- * * HgfsConvertToNtTime -- * * Convert from Unix time to Windows NT time. * * Results: * The time in Windows NT format. * * Side effects: * None * *----------------------------------------------------------------------------- */ uint64 HgfsConvertToNtTime(time_t unixTime, // IN: Time in Unix format (seconds) long nsec) // IN: nanoseconds { return (uint64)unixTime * 10000000 + nsec / 100 + UNIX_EPOCH; } /* *----------------------------------------------------------------------------- * * HgfsConvertFromNtTimeNsec -- * * Convert from Windows NT time to Unix time. If NT time is outside of * UNIX time range (1970-2038), returned time is nearest time valid in * UNIX. * * Results: * 0 on success * non-zero if NT time is outside of valid range for UNIX * * Side effects: * None * *----------------------------------------------------------------------------- */ int HgfsConvertFromNtTimeNsec(struct timespec *unixTime, // OUT: Time in UNIX format uint64 ntTime) // IN: Time in Windows NT format { #ifndef VM_X86_64 uint32 sec; uint32 nsec; ASSERT(unixTime); /* We assume that time_t is 32bit */ ASSERT_ON_COMPILE(sizeof (unixTime->tv_sec) == 4); /* Cap NT time values that are outside of Unix time's range */ if (ntTime >= UNIX_S32_MAX) { unixTime->tv_sec = 0x7FFFFFFF; unixTime->tv_nsec = 0; return 1; } #else ASSERT(unixTime); #endif if (ntTime < UNIX_EPOCH) { unixTime->tv_sec = 0; unixTime->tv_nsec = 0; return -1; } #ifndef VM_X86_64 Div643232(ntTime - UNIX_EPOCH, 10000000, &sec, &nsec); unixTime->tv_sec = sec; unixTime->tv_nsec = nsec * 100; #else unixTime->tv_sec = (ntTime - UNIX_EPOCH) / 10000000; unixTime->tv_nsec = ((ntTime - UNIX_EPOCH) % 10000000) * 100; #endif return 0; } /* *----------------------------------------------------------------------------- * * HgfsConvertFromNtTime -- * * Convert from Windows NT time to Unix time. * * Results: * 0 on success * nonzero if time is not representable on UNIX * * Side effects: * None * *----------------------------------------------------------------------------- */ int HgfsConvertFromNtTime(time_t *unixTime, // OUT: Time in UNIX format uint64 ntTime) // IN: Time in Windows NT format { struct timespec tm; int ret; ret = HgfsConvertFromNtTimeNsec(&tm, ntTime); *unixTime = tm.tv_sec; return ret; } #endif /* !def(_WIN32) */ #undef UNIX_EPOCH #undef UNIX_S32_MAX /* *----------------------------------------------------------------------------- * * HgfsConvertFromInternalStatus -- * * This function converts between a platform-specific status code and a * cross-platform status code to be sent down the wire. * * Results: * Converted status code. * * Side effects: * None. * *----------------------------------------------------------------------------- */ #ifdef _WIN32 HgfsStatus HgfsConvertFromInternalStatus(HgfsInternalStatus status) // IN { switch(status) { case ERROR_SUCCESS: return HGFS_STATUS_SUCCESS; case ERROR_FILE_NOT_FOUND: case ERROR_PATH_NOT_FOUND: return HGFS_STATUS_NO_SUCH_FILE_OR_DIR; case ERROR_INVALID_HANDLE: return HGFS_STATUS_INVALID_HANDLE; case ERROR_ALREADY_EXISTS: case ERROR_FILE_EXISTS: return HGFS_STATUS_FILE_EXISTS; case ERROR_DIR_NOT_EMPTY: return HGFS_STATUS_DIR_NOT_EMPTY; case RPC_S_PROTOCOL_ERROR: return HGFS_STATUS_PROTOCOL_ERROR; case ERROR_ACCESS_DENIED: return HGFS_STATUS_ACCESS_DENIED; case ERROR_INVALID_NAME: return HGFS_STATUS_INVALID_NAME; case ERROR_SHARING_VIOLATION: return HGFS_STATUS_SHARING_VIOLATION; case ERROR_DISK_FULL: case ERROR_HANDLE_DISK_FULL: return HGFS_STATUS_NO_SPACE; case ERROR_NOT_SUPPORTED: return HGFS_STATUS_OPERATION_NOT_SUPPORTED; case HGFS_INTERNAL_STATUS_ERROR: default: return HGFS_STATUS_GENERIC_ERROR; } } #else /* Win32 */ HgfsStatus HgfsConvertFromInternalStatus(HgfsInternalStatus status) // IN { switch(status) { case 0: return HGFS_STATUS_SUCCESS; case ENOENT: return HGFS_STATUS_NO_SUCH_FILE_OR_DIR; case EBADF: return HGFS_STATUS_INVALID_HANDLE; case EPERM: return HGFS_STATUS_OPERATION_NOT_PERMITTED; case EEXIST: return HGFS_STATUS_FILE_EXISTS; case ENOTDIR: return HGFS_STATUS_NOT_DIRECTORY; case ENOTEMPTY: return HGFS_STATUS_DIR_NOT_EMPTY; case EPROTO: return HGFS_STATUS_PROTOCOL_ERROR; case EACCES: return HGFS_STATUS_ACCESS_DENIED; case EINVAL: return HGFS_STATUS_INVALID_NAME; case ENOSPC: return HGFS_STATUS_NO_SPACE; case EOPNOTSUPP: return HGFS_STATUS_OPERATION_NOT_SUPPORTED; case ENAMETOOLONG: return HGFS_STATUS_NAME_TOO_LONG; case HGFS_INTERNAL_STATUS_ERROR: default: return HGFS_STATUS_GENERIC_ERROR; } } #endif vmhgfs-only/staticEscape.h0000444000000000000000000000466112025726745014625 0ustar rootroot/********************************************************* * Copyright (C) 1998 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 * *********************************************************/ /* * staticEscape.h -- * * Buffer escaping using bit vectors instead of arrays * and static buffers instead of dynbufs. [bac] * * - Unescaping is done in place and cannot fail. * - Escaping's results are put into the caller-provided static * buffer, and it fails if the buffer is too small. */ #ifndef __STATIC_ESCAPE_H__ #define __STATIC_ESCAPE_H__ #include "escBitvector.h" int StaticEscape_Do(char escByte, // IN EscBitVector const *bytesToEsc, // IN void const *bufIn, // IN unsigned int sizeIn, // IN unsigned int sizeBufout, // IN char *bufOut); // OUT int StaticEscape_Undo(char escByte, // IN void *bufIn, // IN unsigned int sizeIn); // IN #if defined(_WIN32) /* Wide character versions of the escape routines. */ int StaticEscape_DoW(wchar_t escByte, // IN wchar_t const *bytesToEsc, // IN void const *bufIn, // IN unsigned int sizeIn, // IN unsigned int sizeBufout, // IN void *bufOut); // OUT int StaticEscape_UndoW(wchar_t escByte, // IN wchar_t *bufIn, // IN unsigned int sizeIn); // IN int StaticEscape_UndoWToA(char escChar, // IN char *bufIn, // IN uint32 sizeIn); // IN #endif #endif /* __STATIC_ESCAPE_H__ */ vmhgfs-only/dbllnklst.h0000444000000000000000000000445612025726745014210 0ustar rootroot/********************************************************* * Copyright (C) 1998 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 * *********************************************************/ /* * dbllnklst.h -- * * Double linked lists */ #ifndef _DBLLNKLST_H_ #define _DBLLNKLST_H_ #include "vm_basic_types.h" #define INCLUDE_ALLOW_MODULE #define INCLUDE_ALLOW_USERLEVEL #include "includeCheck.h" #define DblLnkLst_OffsetOf(type, field) ((intptr_t)&((type *)0)->field) #define DblLnkLst_Container(addr, type, field) \ ((type *)((char *)addr - DblLnkLst_OffsetOf(type, field))) #define DblLnkLst_ForEach(curr, head) \ for (curr = (head)->next; curr != (head); curr = (curr)->next) /* Safe from list element removal within loop body. */ #define DblLnkLst_ForEachSafe(curr, nextElem, head) \ for (curr = (head)->next, nextElem = (curr)->next; \ curr != (head); \ curr = nextElem, nextElem = (curr)->next) typedef struct DblLnkLst_Links { struct DblLnkLst_Links *prev; struct DblLnkLst_Links *next; } DblLnkLst_Links; /* Functions for both circular and anchored lists. --hpreg */ void DblLnkLst_Init(DblLnkLst_Links *l); void DblLnkLst_Link(DblLnkLst_Links *l1, DblLnkLst_Links *l2); void DblLnkLst_Unlink(DblLnkLst_Links *l1, DblLnkLst_Links *l2); void DblLnkLst_Unlink1(DblLnkLst_Links *l); Bool DblLnkLst_IsLinked(DblLnkLst_Links *l); /* Functions specific to anchored lists. --hpreg */ void DblLnkLst_LinkFirst(DblLnkLst_Links *head, DblLnkLst_Links *l); void DblLnkLst_LinkLast(DblLnkLst_Links *head, DblLnkLst_Links *l); #endif /* _DBLLNKLST_H_ */ vmhgfs-only/bdhandler.c0000444000000000000000000002164312025726746014133 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ /* * bdhandler.c -- * * Background thread for handling backdoor requests and replies. */ /* Must come before any kernel header file. */ #include "driver-config.h" #include #include #include "compat_completion.h" #include "compat_kernel.h" #include "compat_kthread.h" #include "compat_list.h" #include "compat_sched.h" #include "compat_semaphore.h" #include "compat_slab.h" #include "compat_spinlock.h" #include "compat_version.h" /* Must be included after semaphore.h. */ #include /* Must be included after sched.h. */ #include #include "hgfsBd.h" #include "hgfsDevLinux.h" #include "hgfsProto.h" #include "bdhandler.h" #include "module.h" #include "request.h" #include "vm_assert.h" #include "rpcout.h" #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 9) int errno; /* compat_exit() needs global errno variable. */ #endif static inline void HgfsWakeWaitingClient(HgfsReq *req); static inline void HgfsCompleteReq(HgfsReq *req, char const *reply, size_t replySize); static void HgfsSendUnsentReqs(void); /* * Private function implementations. */ /* *---------------------------------------------------------------------- * * HgfsWakeWaitingClient -- * * Wakes up the client process waiting for the reply to this * request. * * Results: * None * * Side effects: * None * *---------------------------------------------------------------------- */ static inline void HgfsWakeWaitingClient(HgfsReq *req) // IN: Request { ASSERT(req); wake_up(&req->queue); } /* *---------------------------------------------------------------------- * * HgfsCompleteReq -- * * Copies the reply packet into the request structure and wakes up * the associated client. * * Results: * None * * Side effects: * None * *---------------------------------------------------------------------- */ static inline void HgfsCompleteReq(HgfsReq *req, // IN: Request char const *reply, // IN: Reply packet size_t replySize) // IN: Size of reply packet { ASSERT(replySize <= HGFS_PACKET_MAX); memcpy(HGFS_REQ_PAYLOAD(req), reply, replySize); req->payloadSize = replySize; req->state = HGFS_REQ_STATE_COMPLETED; list_del_init(&req->list); HgfsWakeWaitingClient(req); } /* *----------------------------------------------------------------------------- * * HgfsSendUnsentReqs -- * * Process the unsent list and send requests to the backdoor. * * Results: * None. * * Side effects: * None. * *----------------------------------------------------------------------------- */ static void HgfsSendUnsentReqs(void) { char const *replyPacket; struct list_head *cur, *tmp; HgfsReq *req; size_t payloadSize; spin_lock(&hgfsBigLock); list_for_each_safe(cur, tmp, &hgfsReqsUnsent) { req = list_entry(cur, HgfsReq, list); /* * A big "wtf" from the driver is in order. Perhaps by "wtf" I really * mean BUG_ON(). */ ASSERT(req->state == HGFS_REQ_STATE_UNSENT); if (req->state != HGFS_REQ_STATE_UNSENT) { LOG(2, (KERN_DEBUG "VMware hgfs: HgfsSendUnsentReqs: Found request " "on unsent list in the wrong state, ignoring\n")); continue; } ASSERT(req->payloadSize <= HGFS_PACKET_MAX); payloadSize = req->payloadSize; LOG(8, (KERN_DEBUG "VMware hgfs: HgfsSendUnsentReqs: Sending packet " "over backdoor\n")); /* * We should attempt to reopen the backdoor channel with every request, * because the HGFS server in the host can be enabled or disabled at any * time. */ if (!HgfsBd_OpenBackdoor(&hgfsRpcOut)) { req->state = HGFS_REQ_STATE_ERROR; list_del_init(&req->list); printk(KERN_WARNING "VMware hgfs: HGFS is disabled in the host\n"); HgfsWakeWaitingClient(req); } else if (HgfsBd_Dispatch(hgfsRpcOut, HGFS_REQ_PAYLOAD(req), &payloadSize, &replyPacket) == 0) { /* Request sent successfully. Copy the reply and wake the client. */ HgfsCompleteReq(req, replyPacket, payloadSize); LOG(8, (KERN_DEBUG "VMware hgfs: HgfsSendUnsentReqs: Backdoor " "reply received\n")); } else { /* Pass the error into the request. */ req->state = HGFS_REQ_STATE_ERROR; list_del_init(&req->list); LOG(8, (KERN_DEBUG "VMware hgfs: HgfsSendUnsentReqs: Backdoor " "error\n")); HgfsWakeWaitingClient(req); /* * If the channel was previously open, make sure it's dead and gone * now. We do this because subsequent requests deserve a chance to * reopen it. */ HgfsBd_CloseBackdoor(&hgfsRpcOut); } } spin_unlock(&hgfsBigLock); } /* * Public function implementations. */ /* *----------------------------------------------------------------------------- * * HgfsResetOps -- * * Reset ops with more than one opcode back to the desired opcode. * * Results: * None. * * Side effects: * None. * *----------------------------------------------------------------------------- */ void HgfsResetOps(void) { atomic_set(&hgfsVersionOpen, HGFS_OP_OPEN_V3); atomic_set(&hgfsVersionRead, HGFS_OP_READ_V3); atomic_set(&hgfsVersionWrite, HGFS_OP_WRITE_V3); atomic_set(&hgfsVersionClose, HGFS_OP_CLOSE_V3); atomic_set(&hgfsVersionSearchOpen, HGFS_OP_SEARCH_OPEN_V3); atomic_set(&hgfsVersionSearchRead, HGFS_OP_SEARCH_READ_V3); atomic_set(&hgfsVersionSearchClose, HGFS_OP_SEARCH_CLOSE_V3); atomic_set(&hgfsVersionGetattr, HGFS_OP_GETATTR_V3); atomic_set(&hgfsVersionSetattr, HGFS_OP_SETATTR_V3); atomic_set(&hgfsVersionCreateDir, HGFS_OP_CREATE_DIR_V3); atomic_set(&hgfsVersionDeleteFile, HGFS_OP_DELETE_FILE_V3); atomic_set(&hgfsVersionDeleteDir, HGFS_OP_DELETE_DIR_V3); atomic_set(&hgfsVersionRename, HGFS_OP_RENAME_V3); atomic_set(&hgfsVersionQueryVolumeInfo, HGFS_OP_QUERY_VOLUME_INFO_V3); atomic_set(&hgfsVersionCreateSymlink, HGFS_OP_CREATE_SYMLINK_V3); } /* *---------------------------------------------------------------------- * * HgfsBdHandler -- * * Function run in background thread to pick up HGFS requests from * the filesystem half of the driver, send them over the backdoor, * get replies, and send them back to the filesystem. * * Note that this function is called out of the kthread subsystem or, in * older kernels, a similar abstraction built in compat_kthread.h. * * Results: * Always returns zero. * * Side effects: * Processes entries from hgfsReqQ. * *---------------------------------------------------------------------- */ int HgfsBdHandler(void *data) // Ignored { LOG(6, (KERN_DEBUG "VMware hgfs: HgfsBdHandler: Thread starting\n")); compat_set_freezable(); for (;;) { /* Sleep, waiting for a request or exit. */ wait_event_interruptible(hgfsReqThreadWait, test_bit(HGFS_REQ_THREAD_SEND, &hgfsReqThreadFlags) || compat_kthread_should_stop()); /* * First, check for suspend. I'm not convinced that this actually * has to come first, but whatever. */ if (compat_try_to_freeze()) { LOG(6, (KERN_DEBUG "VMware hgfs: HgfsBdHandler: Closing backdoor after resume\n")); HgfsBd_CloseBackdoor(&hgfsRpcOut); } /* Send outgoing requests. */ if (test_and_clear_bit(HGFS_REQ_THREAD_SEND, &hgfsReqThreadFlags)) { LOG(8, (KERN_DEBUG "VMware hgfs: HgfsBdHandler: Sending requests\n")); HgfsSendUnsentReqs(); } /* Kill yourself. */ if (compat_kthread_should_stop()) { LOG(6, (KERN_DEBUG "VMware hgfs: HgfsBdHandler: Told to exit\n")); break; } } LOG(6, (KERN_DEBUG "VMware hgfs: HgfsBdHandler: Closing backdoor\n")); HgfsBd_CloseBackdoor(&hgfsRpcOut); LOG(6, (KERN_DEBUG "VMware hgfs: HgfsBdHandler: Thread exiting\n")); return 0; } vmhgfs-only/bdhandler.h0000444000000000000000000000221712025726746014134 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ /* * bdhandler.h -- * * Background thread for handling backdoor requests and replies. */ #ifndef _HGFS_DRIVER_BDHANDLER_H_ #define _HGFS_DRIVER_BDHANDLER_H_ /* Public functions (with respect to the entire module). */ void HgfsResetOps(void); int HgfsBdHandler(void *data); #endif // _HGFS_DRIVER_BDHANDLER_H_ vmhgfs-only/dentry.c0000444000000000000000000000605012025726746013510 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ /* * dentry.c -- * * Dentry operations for the filesystem portion of the vmhgfs driver. */ /* Must come before any kernel header file. */ #include "driver-config.h" #include "compat_fs.h" #include "compat_kernel.h" #include "compat_version.h" #include "inode.h" #include "module.h" #include "vm_assert.h" /* HGFS dentry operations. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 75) static int HgfsDentryRevalidate(struct dentry *dentry, struct nameidata *nd); #else static int HgfsDentryRevalidate(struct dentry *dentry, int flags); #endif /* HGFS dentry operations structure. */ struct dentry_operations HgfsDentryOperations = { .d_revalidate = HgfsDentryRevalidate, }; /* * HGFS dentry operations. */ /* *---------------------------------------------------------------------- * * HgfsDentryRevalidate -- * * Called by namei.c every time a dentry is looked up in the dcache * to determine if it is still valid. * * If the entry is found to be invalid, namei calls dput on it and * returns NULL, which causes a new lookup to be done in the actual * filesystem, which in our case means that HgfsLookup is called. * * Results: * Positive value if the entry IS valid. * Zero if the entry is NOT valid. * * Side effects: * None * *---------------------------------------------------------------------- */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 75) static int HgfsDentryRevalidate(struct dentry *dentry, // IN: Dentry to revalidate struct nameidata *nd) // IN: Lookup flags & intent #else static int HgfsDentryRevalidate(struct dentry *dentry, // IN: Dentry to revalidate int flags) // IN: Lookup flags (e.g. LOOKUP_CONTINUE) #endif { int error; LOG(6, (KERN_DEBUG "VMware hgfs: HgfsDentryRevalidate: calling " "HgfsRevalidate\n")); ASSERT(dentry); /* Just call HgfsRevaliate, which does the right thing. */ error = HgfsRevalidate(dentry); if (error) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDentryRevalidate: invalid\n")); return 0; } LOG(6, (KERN_DEBUG "VMware hgfs: HgfsDentryRevalidate: valid\n")); return 1; } vmhgfs-only/dir.c0000444000000000000000000006605312025726746012772 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ /* * dir.c -- * * Directory operations for the filesystem portion of the vmhgfs driver. */ /* Must come before any kernel header file. */ #include "driver-config.h" #include #include #include "compat_fs.h" #include "compat_kernel.h" #include "compat_slab.h" #include "cpName.h" #include "hgfsProto.h" #include "hgfsUtil.h" #include "module.h" #include "request.h" #include "fsutil.h" #include "vm_assert.h" #include "vm_basic_types.h" /* Private functions. */ static int HgfsUnpackSearchReadReply(HgfsReq *req, HgfsAttrInfo *attr); static int HgfsGetNextDirEntry(HgfsSuperInfo *si, HgfsHandle searchHandle, uint32 offset, HgfsAttrInfo *attr, Bool *done); static int HgfsPackDirOpenRequest(struct inode *inode, struct file *file, HgfsOp opUsed, HgfsReq *req); /* HGFS file operations for directories. */ static int HgfsDirOpen(struct inode *inode, struct file *file); static int HgfsReaddir(struct file *file, void *dirent, filldir_t filldir); static int HgfsDirRelease(struct inode *inode, struct file *file); /* HGFS file operations structure for directories. */ struct file_operations HgfsDirFileOperations = { .owner = THIS_MODULE, .open = HgfsDirOpen, .read = generic_read_dir, .readdir = HgfsReaddir, .release = HgfsDirRelease, }; /* * Private function implementations. */ /* *---------------------------------------------------------------------- * * HgfsUnpackSearchReadReply -- * * This function abstracts the differences between a SearchReadV1 and * a SearchReadV2. The caller provides the packet containing the reply * and we populate the AttrInfo with version-independent information. * * Note that attr->requestType has already been populated so that we * know whether to expect a V1 or V2 reply. * * Results: * 0 on success, anything else on failure. * * Side effects: * None * *---------------------------------------------------------------------- */ static int HgfsUnpackSearchReadReply(HgfsReq *req, // IN: Reply packet HgfsAttrInfo *attr) // IN/OUT: Attributes { char *fileName; uint32 fileNameLength; uint32 replySize; int result; ASSERT(req); ASSERT(attr); result = HgfsUnpackCommonAttr(req, attr); if (result != 0) { return result; } switch(attr->requestType) { case HGFS_OP_SEARCH_READ_V3: { HgfsReplySearchReadV3 *replyV3; HgfsDirEntry *dirent; /* Currently V3 returns only 1 entry. */ replyV3 = (HgfsReplySearchReadV3 *)(HGFS_REP_PAYLOAD_V3(req)); replyV3->count = 1; replySize = HGFS_REP_PAYLOAD_SIZE_V3(replyV3) + sizeof *dirent; dirent = (HgfsDirEntry *)replyV3->payload; fileName = dirent->fileName.name; fileNameLength = dirent->fileName.length; break; } case HGFS_OP_SEARCH_READ_V2: { HgfsReplySearchReadV2 *replyV2; replyV2 = (HgfsReplySearchReadV2 *)(HGFS_REQ_PAYLOAD(req)); replySize = sizeof *replyV2; fileName = replyV2->fileName.name; fileNameLength = replyV2->fileName.length; break; } case HGFS_OP_SEARCH_READ: { HgfsReplySearchRead *replyV1; replyV1 = (HgfsReplySearchRead *)(HGFS_REQ_PAYLOAD(req)); replySize = sizeof *replyV1; fileName = replyV1->fileName.name; fileNameLength = replyV1->fileName.length; break; } default: LOG(4, (KERN_DEBUG "VMware hgfs: HgfsUnpackSearchReadReply: unexpected " "OP type encountered\n")); return -EPROTO; } /* * Make sure name length is legal. */ if (fileNameLength > NAME_MAX || fileNameLength > HGFS_PACKET_MAX - replySize) { return -ENAMETOOLONG; } /* * If the size of the name is valid (meaning the end of the directory has * not yet been reached), copy the name to the AttrInfo struct. * * XXX: This operation happens often and the length of the filename is * bounded by NAME_MAX. Perhaps I should just put a statically-sized * array in HgfsAttrInfo and use a slab allocator to allocate the struct. */ if (fileNameLength > 0) { /* Sanity check on name length. */ if (fileNameLength != strlen(fileName)) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsUnpackSearchReadReply: name " "length mismatch %u/%Zu, name \"%s\"\n", fileNameLength, strlen(fileName), fileName)); return -EPROTO; } attr->fileName = kmalloc(fileNameLength + 1, GFP_KERNEL); if (attr->fileName == NULL) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsUnpackSearchReadReply: out of " "memory allocating filename, ignoring\n")); return -ENOMEM; } memcpy(attr->fileName, fileName, fileNameLength + 1); } else { attr->fileName = NULL; } return 0; } /* *---------------------------------------------------------------------- * * HgfsGetNextDirEntry -- * * Get the directory entry with the given offset from the server. * * attr->fileName gets allocated and must be freed by the caller. * * Results: * Returns zero on success, negative error on failure. If the * dentry's name is too long, -ENAMETOOLONG is returned. * * Side effects: * None * *---------------------------------------------------------------------- */ static int HgfsGetNextDirEntry(HgfsSuperInfo *si, // IN: Superinfo for this SB HgfsHandle searchHandle, // IN: Handle of dir uint32 offset, // IN: Offset of next dentry to get HgfsAttrInfo *attr, // OUT: File attributes of dentry Bool *done) // OUT: Set true when there are // no more dentries { HgfsReq *req; HgfsOp opUsed; HgfsStatus replyStatus; int result = 0; ASSERT(si); ASSERT(attr); ASSERT(done); req = HgfsGetNewRequest(); if (!req) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsGetNextDirEntry: out of memory " "while getting new request\n")); return -ENOMEM; } retry: opUsed = atomic_read(&hgfsVersionSearchRead); if (opUsed == HGFS_OP_SEARCH_READ_V3) { HgfsRequest *header; HgfsRequestSearchReadV3 *request; header = (HgfsRequest *)(HGFS_REQ_PAYLOAD(req)); header->op = attr->requestType = opUsed; header->id = req->id; request = (HgfsRequestSearchReadV3 *)(HGFS_REQ_PAYLOAD_V3(req)); request->search = searchHandle; request->offset = offset; request->flags = 0; request->reserved = 0; req->payloadSize = HGFS_REQ_PAYLOAD_SIZE_V3(request); } else { HgfsRequestSearchRead *request; request = (HgfsRequestSearchRead *)(HGFS_REQ_PAYLOAD(req)); request->header.op = attr->requestType = opUsed; request->header.id = req->id; request->search = searchHandle; request->offset = offset; req->payloadSize = sizeof *request; } /* Send the request and process the reply. */ result = HgfsSendRequest(req); if (result == 0) { LOG(6, (KERN_DEBUG "VMware hgfs: HgfsGetNextDirEntry: got reply\n")); replyStatus = HgfsReplyStatus(req); result = HgfsStatusConvertToLinux(replyStatus); switch(result) { case 0: result = HgfsUnpackSearchReadReply(req, attr); if (result == 0 && attr->fileName == NULL) { /* We're at the end of the directory. */ LOG(6, (KERN_DEBUG "VMware hgfs: HgfsGetNextDirEntry: end of " "dir\n")); *done = TRUE; } break; case -EPROTO: /* Retry with older version(s). Set globally. */ if (attr->requestType == HGFS_OP_SEARCH_READ_V3) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsGetNextDirEntry: Version 3 " "not supported. Falling back to version 2.\n")); atomic_set(&hgfsVersionSearchRead, HGFS_OP_SEARCH_READ_V2); goto retry; } else if (attr->requestType == HGFS_OP_SEARCH_READ_V2) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsGetNextDirEntry: Version 2 " "not supported. Falling back to version 1.\n")); atomic_set(&hgfsVersionSearchRead, HGFS_OP_SEARCH_READ); goto retry; } /* Fallthrough. */ default: break; } } else if (result == -EIO) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsGetNextDirEntry: timed out\n")); } else if (result == -EPROTO) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsGetNextDirEntry: server " "returned error: %d\n", result)); } else { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsGetNextDirEntry: unknown error: " "%d\n", result)); } HgfsFreeRequest(req); return result; } /* *---------------------------------------------------------------------- * * HgfsPackDirOpenRequest -- * * Setup the directory open request, depending on the op version. * * Results: * Returns zero on success, or negative error on failure. * * Side effects: * None * *---------------------------------------------------------------------- */ static int HgfsPackDirOpenRequest(struct inode *inode, // IN: Inode of the file to open struct file *file, // IN: File pointer for this open HgfsOp opUsed, // IN: Op to be used HgfsReq *req) // IN/OUT: Packet to write into { char *name; uint32 *nameLength; size_t requestSize; int result; ASSERT(inode); ASSERT(file); ASSERT(req); switch (opUsed) { case HGFS_OP_SEARCH_OPEN_V3: { HgfsRequest *requestHeader; HgfsRequestSearchOpenV3 *requestV3; requestHeader = (HgfsRequest *)(HGFS_REQ_PAYLOAD(req)); requestHeader->op = opUsed; requestHeader->id = req->id; requestV3 = (HgfsRequestSearchOpenV3 *)HGFS_REQ_PAYLOAD_V3(req); /* We'll use these later. */ name = requestV3->dirName.name; nameLength = &requestV3->dirName.length; requestV3->dirName.flags = 0; requestV3->dirName.caseType = HGFS_FILE_NAME_CASE_SENSITIVE; requestV3->dirName.fid = HGFS_INVALID_HANDLE; requestV3->reserved = 0; requestSize = HGFS_REQ_PAYLOAD_SIZE_V3(requestV3); break; } case HGFS_OP_SEARCH_OPEN: { HgfsRequestSearchOpen *request; request = (HgfsRequestSearchOpen *)(HGFS_REQ_PAYLOAD(req)); request->header.op = opUsed; request->header.id = req->id; /* We'll use these later. */ name = request->dirName.name; nameLength = &request->dirName.length; requestSize = sizeof *request; break; } default: LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPackDirOpenRequest: unexpected " "OP type encountered\n")); return -EPROTO; } /* Build full name to send to server. */ if (HgfsBuildPath(name, HGFS_PACKET_MAX - (requestSize - 1), file->f_dentry) < 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPackDirOpenRequest: build path failed\n")); return -EINVAL; } LOG(6, (KERN_DEBUG "VMware hgfs: HgfsPackDirOpenRequest: opening \"%s\"\n", name)); /* Convert to CP name. */ result = CPName_ConvertTo(name, HGFS_PACKET_MAX - (requestSize - 1), name); if (result < 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPackDirOpenRequest: CP conversion failed\n")); return -EINVAL; } /* Unescape the CP name. */ result = HgfsUnescapeBuffer(name, result); *nameLength = (uint32) result; req->payloadSize = requestSize + result; return 0; } /* * HGFS file operations for directories. */ /* *---------------------------------------------------------------------- * * HgfsDirOpen -- * * Called whenever a process opens a directory in our filesystem. * * We send a "Search Open" request to the server with the name * stored in this file's inode. If the Open succeeds, we store the * search handle sent by the server in the file struct so it can be * accessed by readdir and close. * * Results: * Returns zero if on success, error on failure. * * Side effects: * None * *---------------------------------------------------------------------- */ static int HgfsDirOpen(struct inode *inode, // IN: Inode of the dir to open struct file *file) // IN: File pointer for this open { HgfsReq *req; int result; HgfsOp opUsed; HgfsStatus replyStatus; HgfsHandle *replySearch; ASSERT(inode); ASSERT(inode->i_sb); ASSERT(file); req = HgfsGetNewRequest(); if (!req) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDirOpen: out of memory while " "getting new request\n")); result = -ENOMEM; goto out; } retry: opUsed = atomic_read(&hgfsVersionSearchOpen); if (opUsed == HGFS_OP_SEARCH_OPEN_V3) { replySearch = &((HgfsReplySearchOpenV3 *)HGFS_REP_PAYLOAD_V3(req))->search; } else { replySearch = &((HgfsReplySearchOpen *)HGFS_REQ_PAYLOAD(req))->search; } result = HgfsPackDirOpenRequest(inode, file, opUsed, req); if (result != 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDirOpen: error packing request\n")); goto out; } /* Send the request and process the reply. */ result = HgfsSendRequest(req); if (result == 0) { /* Get the reply and check return status. */ replyStatus = HgfsReplyStatus(req); result = HgfsStatusConvertToLinux(replyStatus); switch (result) { case 0: result = HgfsCreateFileInfo(file, *replySearch); if (result) { goto out; } LOG(6, (KERN_DEBUG "VMware hgfs: HgfsDirOpen: set handle to %u\n", *replySearch)); break; case -EPROTO: /* Retry with older version(s). Set globally. */ if (opUsed == HGFS_OP_SEARCH_OPEN_V3) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDirOpen: Version 3 not " "supported. Falling back to version 1.\n")); atomic_set(&hgfsVersionSearchOpen, HGFS_OP_SEARCH_OPEN); goto retry; } LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDirOpen: server " "returned error: %d\n", result)); break; default: LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDirOpen: server " "returned error: %d\n", result)); break; } } else if (result == -EIO) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDirOpen: timed out\n")); } else if (result == -EPROTO) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDirOpen: server " "returned error: %d\n", result)); } else { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDirOpen: unknown error: " "%d\n", result)); } out: HgfsFreeRequest(req); return result; } /* *---------------------------------------------------------------------- * * HgfsReaddir -- * * Handle a readdir request. See details below if interested. * * Readdir is a bit complicated, and is best understood by reading * the code. For the impatient, here is an overview of the major * moving parts [bac]: * * - Getdents syscall calls readdir, which is supposed to call * filldir some number of times. * - Each time it's called, filldir updates a struct with the * number of bytes copied thus far, and sets an error code if * appropriate. * - When readdir returns, getdents checks the struct to see if * any dentries were copied, and if so returns the byte count. * Otherwise, it returns the error from the struct (which should * still be zero if filldir was never called). * * A consequence of this last fact is that if there are no more * dentries, then readdir should NOT call filldir, and should * return from readdir with a non-error. * * Other notes: * * - Passing an inum of zero to filldir doesn't work. At a minimum, * you have to make up a bogus inum for each dentry. * - Passing the correct d_type to filldir seems to be non-critical; * apparently most programs (such as ls) stat each file if they * really want to know what type it is. However, passing the * correct type means that ls doesn't bother calling stat on * directories, and that saves an entire round trip per dirctory * dentry. * * Results: * Returns zero if on success, negative error on failure. * (According to /fs/readdir.c, any non-negative return value * means it succeeded). * * Side effects: * None * *---------------------------------------------------------------------- */ static int HgfsReaddir(struct file *file, // IN: Directory to read from void *dirent, // OUT: Buffer to copy dentries into filldir_t filldir) // IN: Filler function { HgfsSuperInfo *si; HgfsAttrInfo attr; uint32 d_type; // type of dirent char *escName = NULL; // buf for escaped version of name size_t escNameLength = NAME_MAX + 1; int nameLength = 0; int result = 0; Bool done = FALSE; ino_t ino; ASSERT(file); ASSERT(dirent); if (!file || !(file->f_dentry) || !(file->f_dentry->d_inode)) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsReaddir: null input\n")); return -EFAULT; } ASSERT(file->f_dentry->d_inode->i_sb); si = HGFS_SB_TO_COMMON(file->f_dentry->d_inode->i_sb); LOG(6, (KERN_DEBUG "VMware hgfs: HgfsReaddir: dir with name %s, " "inum %lu, f_pos %Lu\n", file->f_dentry->d_name.name, file->f_dentry->d_inode->i_ino, file->f_pos)); /* * Some day when we're out of things to do we can move this to a slab * allocator. */ escName = kmalloc(escNameLength, GFP_KERNEL); if (!escName) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsReaddir: out of memory allocating " "escaped name buffer\n")); return -ENOMEM; } while (1) { /* * Nonzero result = we failed to get valid reply from server. * Zero result: * - done == TRUE means we hit the end of the directory * - Otherwise, attr.fileName has the name of the next dirent * */ result = HgfsGetNextDirEntry(si, FILE_GET_FI_P(file)->handle, (uint32)file->f_pos, &attr, &done); if (result == -ENAMETOOLONG) { /* * Skip dentry if its name is too long (see below). * * XXX: If a bad server sends us bad packets, we can loop here * forever, as I did while testing *grumble*. Maybe we should error * in that case. */ file->f_pos++; continue; } else if (result) { /* Error */ LOG(4, (KERN_DEBUG "VMware hgfs: HgfsReaddir: error " "getting dentry\n")); kfree(escName); return result; } if (done == TRUE) { LOG(6, (KERN_DEBUG "VMware hgfs: HgfsReaddir: end of dir reached\n")); break; } /* * Escape all non-printable characters (which for linux is just * "/"). * * Note that normally we would first need to convert from the * CP name format, but that is done implicitely here since we * are guaranteed to have just one path component per dentry. */ result = HgfsEscapeBuffer(attr.fileName, strlen(attr.fileName), escNameLength, escName); kfree(attr.fileName); /* * Check the filename length. * * If the name is too long to be represented in linux, we simply * skip it (i.e., that file is not visible to our filesystem) by * incrementing file->f_pos and repeating the loop to get the * next dentry. * * HgfsEscapeBuffer returns a negative value if the escaped * output didn't fit in the specified output size, so we can * just check its return value. */ if (result < 0) { /* * XXX: Another area where a bad server could cause us to loop * forever. */ file->f_pos++; continue; } nameLength = result; /* Assign the correct dentry type. */ switch (attr.type) { case HGFS_FILE_TYPE_SYMLINK: d_type = DT_LNK; break; case HGFS_FILE_TYPE_REGULAR: d_type = DT_REG; break; case HGFS_FILE_TYPE_DIRECTORY: d_type = DT_DIR; break; default: /* * XXX Should never happen. I'd put NOT_IMPLEMENTED() here * but if the driver ever goes in the host it's probably not * a good idea for an attacker to be able to hang the host * simply by using a bogus file type in a reply. [bac] */ d_type = DT_UNKNOWN; break; } /* * It is unfortunate, but the HGFS server sends back '.' and ".." * when we do a SearchRead. In an ideal world, these would be faked * on the client, but it would be a real backwards-compatibility * hassle to change the behavior at this point. * * So instead, we'll take the '.' and ".." and modify their inode * numbers so they match what the client expects. */ if (!strncmp(escName, ".", sizeof ".")) { ino = file->f_dentry->d_inode->i_ino; } else if (!strncmp(escName, "..", sizeof "..")) { ino = compat_parent_ino(file->f_dentry); } else { if (attr.mask & HGFS_ATTR_VALID_FILEID) { ino = attr.hostFileId; } else { ino = iunique(file->f_dentry->d_inode->i_sb, HGFS_RESERVED_INO); } } /* * Call filldir for this dentry. */ LOG(6, (KERN_DEBUG "VMware hgfs: HgfsReaddir: calling filldir " "with \"%s\", %u, %Lu\n", escName, nameLength, file->f_pos)); result = filldir(dirent, /* filldir callback struct */ escName, /* name of dirent */ nameLength, /* length of name */ file->f_pos, /* offset of dirent */ ino, /* inode number (0 makes it not show) */ d_type); /* type of dirent */ if (result) { /* * This means that filldir ran out of room in the user buffer * it was copying into; we just break out and return, but * don't increment f_pos. So the next time the user calls * getdents, this dentry will be requested again, will get * retrieved again, and get copied properly to the user. */ break; } file->f_pos++; } LOG(6, (KERN_DEBUG "VMware hgfs: HgfsReaddir: finished\n")); kfree(escName); return 0; } /* *---------------------------------------------------------------------- * * HgfsDirRelease -- * * Called when the last reader of a directory closes it, i.e. when * the directory's file f_count field becomes zero. * * Results: * Returns zero on success, or an error on failure. * * Side effects: * None * *---------------------------------------------------------------------- */ static int HgfsDirRelease(struct inode *inode, // IN: Inode that the file* points to struct file *file) // IN: File for the dir getting released { HgfsReq *req; HgfsStatus replyStatus; HgfsHandle handle; HgfsOp opUsed; int result = 0; ASSERT(inode); ASSERT(file); ASSERT(file->f_dentry); ASSERT(file->f_dentry->d_sb); handle = FILE_GET_FI_P(file)->handle; LOG(6, (KERN_DEBUG "VMware hgfs: HgfsDirRelease: close fh %u\n", handle)); HgfsReleaseFileInfo(file); req = HgfsGetNewRequest(); if (!req) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDirRelease: out of memory while " "getting new request\n")); result = -ENOMEM; goto out; } retry: opUsed = atomic_read(&hgfsVersionSearchClose); if (opUsed == HGFS_OP_SEARCH_CLOSE_V3) { HgfsRequestSearchCloseV3 *request; HgfsRequest *header; header = (HgfsRequest *)(HGFS_REQ_PAYLOAD(req)); header->id = req->id; header->op = opUsed; request = (HgfsRequestSearchCloseV3 *)(HGFS_REQ_PAYLOAD_V3(req)); request->search = handle; request->reserved = 0; req->payloadSize = HGFS_REQ_PAYLOAD_SIZE_V3(request); } else { HgfsRequestSearchClose *request; request = (HgfsRequestSearchClose *)(HGFS_REQ_PAYLOAD(req)); request->header.id = req->id; request->header.op = opUsed; request->search = handle; req->payloadSize = sizeof *request; } /* Send the request and process the reply. */ result = HgfsSendRequest(req); if (result == 0) { /* Get the reply. */ replyStatus = HgfsReplyStatus(req); result = HgfsStatusConvertToLinux(replyStatus); switch (result) { case 0: LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDirRelease: release handle %u\n", handle)); break; case -EPROTO: /* Retry with older version(s). Set globally. */ if (opUsed == HGFS_OP_SEARCH_CLOSE_V3) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDirRelease: Version 3 not " "supported. Falling back to version 1.\n")); atomic_set(&hgfsVersionSearchClose, HGFS_OP_SEARCH_CLOSE); goto retry; } break; default: LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDirRelease: failed handle %u\n", handle)); break; } } else if (result == -EIO) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDirRelease: timed out\n")); } else if (result == -EPROTO) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDirRelease: server " "returned error: %d\n", result)); } else { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsOpen: unknown error: " "%d\n", result)); } out: HgfsFreeRequest(req); return result; } vmhgfs-only/file.c0000444000000000000000000010747112025726746013133 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ /* * file.c -- * * File operations for the filesystem portion of the vmhgfs driver. */ /* Must come before any kernel header file. */ #include "driver-config.h" #include #include #include #include "compat_fs.h" #include "compat_kernel.h" #include "compat_slab.h" #include "cpName.h" #include "hgfsProto.h" #include "module.h" #include "request.h" #include "hgfsUtil.h" #include "fsutil.h" #include "vm_assert.h" #include "vm_basic_types.h" /* Private functions. */ static int HgfsPackOpenRequest(struct inode *inode, struct file *file, HgfsOp opUsed, HgfsReq *req); static int HgfsUnpackOpenReply(HgfsReq *req, HgfsOp opUsed, HgfsHandle *file, HgfsServerLock *lock); static int HgfsGetOpenFlags(uint32 flags); /* HGFS file operations for files. */ static int HgfsOpen(struct inode *inode, struct file *file); #if defined(VMW_USE_AIO) static ssize_t HgfsAioRead(struct kiocb *iocb, const struct iovec *iov, unsigned long numSegs, loff_t offset); static ssize_t HgfsAioWrite(struct kiocb *iocb, const struct iovec *iov, unsigned long numSegs, loff_t offset); #else static ssize_t HgfsRead(struct file *file, char __user *buf, size_t count, loff_t *offset); static ssize_t HgfsWrite(struct file *file, const char __user *buf, size_t count, loff_t *offset); #endif static int HgfsFsync(struct file *file, struct dentry *dentry, int datasync); static int HgfsMmap(struct file *file, struct vm_area_struct *vma); static int HgfsRelease(struct inode *inode, struct file *file); #ifndef VMW_SENDFILE_NONE #if defined(VMW_SENDFILE_OLD) static ssize_t HgfsSendfile(struct file *file, loff_t *offset, size_t count, read_actor_t actor, void __user *target); #else /* defined(VMW_SENDFILE_NEW) */ static ssize_t HgfsSendfile(struct file *file, loff_t *offset, size_t count, read_actor_t actor, void *target); #endif #endif #ifdef VMW_SPLICE_READ static ssize_t HgfsSpliceRead(struct file *file, loff_t *offset, struct pipe_inode_info *pipe, size_t len, unsigned int flags); #endif /* HGFS file operations structure for files. */ struct file_operations HgfsFileFileOperations = { .owner = THIS_MODULE, .open = HgfsOpen, #if defined(VMW_USE_AIO) .aio_read = HgfsAioRead, .aio_write = HgfsAioWrite, #else .read = HgfsRead, .write = HgfsWrite, #endif .fsync = HgfsFsync, .mmap = HgfsMmap, .release = HgfsRelease, #ifndef VMW_SENDFILE_NONE .sendfile = HgfsSendfile, #endif #ifdef VMW_SPLICE_READ .splice_read = HgfsSpliceRead, #endif }; /* File open mask. */ #define HGFS_FILE_OPEN_MASK (HGFS_OPEN_VALID_MODE | \ HGFS_OPEN_VALID_FLAGS | \ HGFS_OPEN_VALID_SPECIAL_PERMS | \ HGFS_OPEN_VALID_OWNER_PERMS | \ HGFS_OPEN_VALID_GROUP_PERMS | \ HGFS_OPEN_VALID_OTHER_PERMS | \ HGFS_OPEN_VALID_FILE_NAME | \ HGFS_OPEN_VALID_SERVER_LOCK) /* * Private functions. */ /* *---------------------------------------------------------------------- * * HgfsPackOpenRequest -- * * Setup the Open request, depending on the op version. * * Results: * Returns zero on success, or negative error on failure. * * Side effects: * None * *---------------------------------------------------------------------- */ static int HgfsPackOpenRequest(struct inode *inode, // IN: Inode of the file to open struct file *file, // IN: File pointer for this open HgfsOp opUsed, // IN: Op to use HgfsReq *req) // IN/OUT: Packet to write into { char *name; uint32 *nameLength; size_t requestSize; int result; ASSERT(inode); ASSERT(file); ASSERT(req); switch (opUsed) { case HGFS_OP_OPEN_V3: { HgfsRequest *requestHeader; HgfsRequestOpenV3 *requestV3; requestHeader = (HgfsRequest *)HGFS_REQ_PAYLOAD(req); requestHeader->op = opUsed; requestHeader->id = req->id; requestV3 = (HgfsRequestOpenV3 *)HGFS_REQ_PAYLOAD_V3(req); requestSize = HGFS_REQ_PAYLOAD_SIZE_V3(requestV3); /* We'll use these later. */ name = requestV3->fileName.name; nameLength = &requestV3->fileName.length; requestV3->mask = HGFS_FILE_OPEN_MASK; /* Linux clients need case-sensitive lookups. */ requestV3->fileName.flags = 0; requestV3->fileName.caseType = HGFS_FILE_NAME_CASE_SENSITIVE; requestV3->fileName.fid = HGFS_INVALID_HANDLE; /* Set mode. */ result = HgfsGetOpenMode(file->f_flags); if (result < 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPackOpenRequest: failed to get " "open mode\n")); return -EINVAL; } requestV3->mode = result; /* Set flags. */ result = HgfsGetOpenFlags(file->f_flags); if (result < 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPackOpenRequest: failed to get " "open flags\n")); return -EINVAL; } requestV3->flags = result; /* Set permissions. */ requestV3->specialPerms = (inode->i_mode & (S_ISUID | S_ISGID | S_ISVTX)) >> 9; requestV3->ownerPerms = (inode->i_mode & S_IRWXU) >> 6; requestV3->groupPerms = (inode->i_mode & S_IRWXG) >> 3; requestV3->otherPerms = (inode->i_mode & S_IRWXO); /* XXX: Request no lock for now. */ requestV3->desiredLock = HGFS_LOCK_NONE; requestV3->reserved1 = 0; requestV3->reserved2 = 0; break; } case HGFS_OP_OPEN_V2: { HgfsRequestOpenV2 *requestV2; requestV2 = (HgfsRequestOpenV2 *)(HGFS_REQ_PAYLOAD(req)); requestV2->header.op = opUsed; requestV2->header.id = req->id; /* We'll use these later. */ name = requestV2->fileName.name; nameLength = &requestV2->fileName.length; requestSize = sizeof *requestV2; requestV2->mask = HGFS_FILE_OPEN_MASK; /* Set mode. */ result = HgfsGetOpenMode(file->f_flags); if (result < 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPackOpenRequest: failed to get " "open mode\n")); return -EINVAL; } requestV2->mode = result; /* Set flags. */ result = HgfsGetOpenFlags(file->f_flags); if (result < 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPackOpenRequest: failed to get " "open flags\n")); return -EINVAL; } requestV2->flags = result; /* Set permissions. */ requestV2->specialPerms = (inode->i_mode & (S_ISUID | S_ISGID | S_ISVTX)) >> 9; requestV2->ownerPerms = (inode->i_mode & S_IRWXU) >> 6; requestV2->groupPerms = (inode->i_mode & S_IRWXG) >> 3; requestV2->otherPerms = (inode->i_mode & S_IRWXO); /* XXX: Request no lock for now. */ requestV2->desiredLock = HGFS_LOCK_NONE; break; } case HGFS_OP_OPEN: { HgfsRequestOpen *request; request = (HgfsRequestOpen *)(HGFS_REQ_PAYLOAD(req)); request->header.op = opUsed; request->header.id = req->id; /* We'll use these later. */ name = request->fileName.name; nameLength = &request->fileName.length; requestSize = sizeof *request; /* Set mode. */ result = HgfsGetOpenMode(file->f_flags); if (result < 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPackOpenRequest: failed to get " "open mode\n")); return -EINVAL; } request->mode = result; /* Set flags. */ result = HgfsGetOpenFlags(file->f_flags); if (result < 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPackOpenRequest: failed to get " "open flags\n")); return -EINVAL; } request->flags = result; /* Set permissions. */ request->permissions = (inode->i_mode & S_IRWXU) >> 6; break; } default: LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPackOpenRequest: unexpected " "OP type encountered\n")); return -EPROTO; } /* Build full name to send to server. */ if (HgfsBuildPath(name, HGFS_PACKET_MAX - (requestSize - 1), file->f_dentry) < 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPackOpenRequest: build path " "failed\n")); return -EINVAL; } LOG(6, (KERN_DEBUG "VMware hgfs: HgfsPackOpenRequest: opening \"%s\", " "flags %o, create perms %o\n", name, file->f_flags, file->f_mode)); /* Convert to CP name. */ result = CPName_ConvertTo(name, HGFS_PACKET_MAX - (requestSize - 1), name); if (result < 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPackOpenRequest: CP conversion " "failed\n")); return -EINVAL; } /* Unescape the CP name. */ result = HgfsUnescapeBuffer(name, result); *nameLength = (uint32) result; req->payloadSize = requestSize + result; return 0; } /* *---------------------------------------------------------------------- * * HgfsUnpackOpenReply -- * * Get interesting fields out of the Open reply, depending on the op * version. * * Results: * Returns zero on success, or negative error on failure. * * Side effects: * None * *---------------------------------------------------------------------- */ static int HgfsUnpackOpenReply(HgfsReq *req, // IN: Packet with reply inside HgfsOp opUsed, // IN: What request op did we send HgfsHandle *file, // OUT: Handle in reply packet HgfsServerLock *lock) // OUT: The server lock we got { HgfsReplyOpenV3 *replyV3; HgfsReplyOpenV2 *replyV2; HgfsReplyOpen *replyV1; size_t replySize; ASSERT(req); ASSERT(file); ASSERT(lock); switch (opUsed) { case HGFS_OP_OPEN_V3: replyV3 = (HgfsReplyOpenV3 *)HGFS_REP_PAYLOAD_V3(req); replySize = HGFS_REP_PAYLOAD_SIZE_V3(replyV3); *file = replyV3->file; *lock = replyV3->acquiredLock; break; case HGFS_OP_OPEN_V2: replyV2 = (HgfsReplyOpenV2 *)(HGFS_REQ_PAYLOAD(req)); replySize = sizeof *replyV2; *file = replyV2->file; *lock = replyV2->acquiredLock; break; case HGFS_OP_OPEN: replyV1 = (HgfsReplyOpen *)(HGFS_REQ_PAYLOAD(req)); replySize = sizeof *replyV1; *file = replyV1->file; *lock = HGFS_LOCK_NONE; break; default: /* This really shouldn't happen since we set opUsed ourselves. */ LOG(4, (KERN_DEBUG "VMware hgfs: HgfsUnpackOpenReply: unexpected " "OP type encountered\n")); ASSERT(FALSE); return -EPROTO; } if (req->payloadSize != replySize) { /* * The reply to Open is a fixed size. So the size of the payload * really ought to match the expected size of an HgfsReplyOpen[V2]. */ LOG(4, (KERN_DEBUG "VMware hgfs: HgfsUnpackOpenReply: wrong packet " "size\n")); return -EPROTO; } return 0; } /* *---------------------------------------------------------------------- * * HgfsGetOpenFlags -- * * Based on the flags requested by the process making the open() * syscall, determine which flags to send to the server to open the * file. * * Results: * Returns the correct HgfsOpenFlags enumeration to send to the * server, or -1 on failure. * * Side effects: * None * *---------------------------------------------------------------------- */ static int HgfsGetOpenFlags(uint32 flags) // IN: Open flags { uint32 mask = O_CREAT | O_TRUNC | O_EXCL; int result = -1; LOG(6, (KERN_DEBUG "VMware hgfs: HgfsGetOpenFlags: entered\n")); /* * Mask the flags to only look at O_CREAT, O_EXCL, and O_TRUNC. */ flags &= mask; /* O_EXCL has no meaning if O_CREAT is not set. */ if (!(flags & O_CREAT)) { flags &= ~O_EXCL; } /* Pick the right HgfsOpenFlags. */ switch (flags) { case 0: /* Regular open; fails if file nonexistant. */ result = HGFS_OPEN; break; case O_CREAT: /* Create file; if it exists already just open it. */ result = HGFS_OPEN_CREATE; break; case O_TRUNC: /* Truncate existing file; fails if nonexistant. */ result = HGFS_OPEN_EMPTY; break; case (O_CREAT | O_EXCL): /* Create file; fail if it exists already. */ result = HGFS_OPEN_CREATE_SAFE; break; case (O_CREAT | O_TRUNC): /* Create file; if it exists already, truncate it. */ result = HGFS_OPEN_CREATE_EMPTY; break; default: /* * This can only happen if all three flags are set, which * conceptually makes no sense because O_EXCL and O_TRUNC are * mutually exclusive if O_CREAT is set. * * However, the open(2) man page doesn't say you can't set all * three flags, and certain apps (*cough* Nautilus *cough*) do * so. To be friendly to those apps, we just silenty drop the * O_TRUNC flag on the assumption that it's safer to honor * O_EXCL. */ LOG(4, (KERN_DEBUG "VMware hgfs: HgfsGetOpenFlags: invalid open " "flags %o. Ignoring the O_TRUNC flag.\n", flags)); result = HGFS_OPEN_CREATE_SAFE; break; } return result; } /* * HGFS file operations for files. */ /* *---------------------------------------------------------------------- * * HgfsOpen -- * * Called whenever a process opens a file in our filesystem. * * We send an "Open" request to the server with the name stored in * this file's inode. If the Open succeeds, we store the filehandle * sent by the server in the file struct so it can be accessed by * read/write/close. * * Results: * Returns zero if on success, error on failure. * * Side effects: * None * *---------------------------------------------------------------------- */ static int HgfsOpen(struct inode *inode, // IN: Inode of the file to open struct file *file) // IN: File pointer for this open { HgfsSuperInfo *si; HgfsReq *req; HgfsOp opUsed; HgfsStatus replyStatus; HgfsHandle replyFile; HgfsServerLock replyLock; HgfsInodeInfo *iinfo; int result = 0; ASSERT(inode); ASSERT(inode->i_sb); ASSERT(file); ASSERT(file->f_dentry); ASSERT(file->f_dentry->d_inode); si = HGFS_SB_TO_COMMON(inode->i_sb); iinfo = INODE_GET_II_P(inode); req = HgfsGetNewRequest(); if (!req) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsOpen: out of memory while " "getting new request\n")); result = -ENOMEM; goto out; } retry: /* * Set up pointers using the proper struct This lets us check the * version exactly once and use the pointers later. */ opUsed = atomic_read(&hgfsVersionOpen); result = HgfsPackOpenRequest(inode, file, opUsed, req); if (result != 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsOpen: error packing request\n")); goto out; } /* Send the request and process the reply. */ result = HgfsSendRequest(req); if (result == 0) { /* Get the reply and check return status. */ replyStatus = HgfsReplyStatus(req); result = HgfsStatusConvertToLinux(replyStatus); switch (result) { case 0: iinfo->createdAndUnopened = FALSE; result = HgfsUnpackOpenReply(req, opUsed, &replyFile, &replyLock); if (result != 0) { break; } result = HgfsCreateFileInfo(file, replyFile); if (result != 0) { break; } LOG(6, (KERN_DEBUG "VMware hgfs: HgfsOpen: set handle to %u\n", replyFile)); /* * HgfsCreate faked all of the inode's attributes, so by the time * we're done in HgfsOpen, we need to make sure that the attributes * in the inode are real. The following is only necessary when * O_CREAT is set, otherwise we got here after HgfsLookup (which sent * a getattr to the server and got the real attributes). * * In particular, we'd like to at least try and set the inode's * uid/gid to match the caller's. We don't expect this to work, * because Windows servers will ignore it, and Linux servers running * as non-root won't be able to change it, but we're forward thinking * people. * * Either way, we force a revalidate following the setattr so that * we'll get the actual uid/gid from the server. */ if (file->f_flags & O_CREAT) { struct dentry *dparent; struct inode *iparent; /* * This is not the root of our file system so there should always * be a parent. */ ASSERT(file->f_dentry->d_parent); /* * Here we obtain a reference on the parent to make sure it doesn't * go away. This might not be necessary, since the existence of * a child (which we hold a reference to in this call) should * account for a reference in the parent, but it's safe to do so. * Overly cautious and safe is better than risky and broken. * * XXX Note that this and a handful of other hacks wouldn't be * necessary if we actually created the file in our create * implementation (where references and locks are properly held). * We could do this if we were willing to give up support for * O_EXCL on 2.4 kernels. */ dparent = dget(file->f_dentry->d_parent); iparent = dparent->d_inode; HgfsSetUidGid(iparent, file->f_dentry, current->fsuid, current->fsgid); dput(dparent); } break; case -EPROTO: /* Retry with older version(s). Set globally. */ if (opUsed == HGFS_OP_OPEN_V3) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsOpen: Version 3 not " "supported. Falling back to version 2.\n")); atomic_set(&hgfsVersionOpen, HGFS_OP_OPEN_V2); goto retry; } /* Retry with Version 1 of Open. Set globally. */ if (opUsed == HGFS_OP_OPEN_V2) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsOpen: Version 2 not " "supported. Falling back to version 1.\n")); atomic_set(&hgfsVersionOpen, HGFS_OP_OPEN); goto retry; } /* Fallthrough. */ default: break; } } else if (result == -EIO) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsOpen: timed out\n")); } else if (result == -EPROTO) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsOpen: server " "returned error: %d\n", result)); } else { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsOpen: unknown error: " "%d\n", result)); } out: HgfsFreeRequest(req); /* * If the open failed (for any reason) and we tried to open a newly created * file, we must ensure that the next operation on this inode triggers a * revalidate to the server. This is because the file wasn't created on the * server, yet we currently believe that it was, because we created a fake * inode with a hashed dentry for it in HgfsCreate. We will continue to * believe this until the dentry's ttl expires, which will cause a * revalidate to the server that will reveal the truth. So in order to find * the truth as soon as possible, we'll reset the dentry's last revalidate * time now to force a revalidate the next time someone uses the dentry. * * We're using our own flag to track this case because using O_CREAT isn't * good enough: HgfsOpen will be called with O_CREAT even if the file exists * on the server, and if that's the case, there's no need to revalidate. * * XXX: Note that this will need to be reworked if/when we support hard * links, because multiple dentries will point to the same inode, and * forcing a revalidate on one will not force it on any others. */ if (result != 0 && iinfo->createdAndUnopened == TRUE) { HgfsDentryAgeForce(file->f_dentry); } return result; } #if defined(VMW_USE_AIO) /* *---------------------------------------------------------------------- * * HgfsAioRead -- * * Called when the kernel initiates an asynchronous read to a file in * our filesystem. Our function is just a thin wrapper around * generic_file_aio_read() that tries to validate the dentry first. * * Results: * Returns the number of bytes read on success, or an error on * failure. * * Side effects: * None * *---------------------------------------------------------------------- */ static ssize_t HgfsAioRead(struct kiocb *iocb, // IN: I/O control block const struct iovec *iov, // OUT: Array of I/O buffers unsigned long numSegs, // IN: Number of buffers loff_t offset) // IN: Offset at which to read { int result; ASSERT(iocb); ASSERT(iocb->ki_filp); ASSERT(iocb->ki_filp->f_dentry); ASSERT(iov); LOG(6, (KERN_DEBUG "VMware hgfs: HgfsAioRead: was called\n")); result = HgfsRevalidate(iocb->ki_filp->f_dentry); if (result) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsAioRead: invalid dentry\n")); goto out; } result = generic_file_aio_read(iocb, iov, numSegs, offset); out: return result; } /* *---------------------------------------------------------------------- * * HgfsAioWrite -- * * Called when the kernel initiates an asynchronous write to a file in * our filesystem. Our function is just a thin wrapper around * generic_file_aio_write() that tries to validate the dentry first. * * Note that files opened with O_SYNC (or superblocks mounted with * "sync") are synchronously written to by the VFS. * * Results: * Returns the number of bytes written on success, or an error on * failure. * * Side effects: * None * *---------------------------------------------------------------------- */ static ssize_t HgfsAioWrite(struct kiocb *iocb, // IN: I/O control block const struct iovec *iov, // IN: Array of I/O buffers unsigned long numSegs, // IN: Number of buffers loff_t offset) // IN: Offset at which to read { int result; ASSERT(iocb); ASSERT(iocb->ki_filp); ASSERT(iocb->ki_filp->f_dentry); ASSERT(iov); LOG(6, (KERN_DEBUG "VMware hgfs: HgfsAioWrite: was called\n")); result = HgfsRevalidate(iocb->ki_filp->f_dentry); if (result) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsAioWrite: invalid dentry\n")); goto out; } result = generic_file_aio_write(iocb, iov, numSegs, offset); out: return result; } #else /* *---------------------------------------------------------------------- * * HgfsRead -- * * Called whenever a process reads from a file in our filesystem. Our * function is just a thin wrapper around generic_read_file() that * tries to validate the dentry first. * * Results: * Returns the number of bytes read on success, or an error on * failure. * * Side effects: * None * *---------------------------------------------------------------------- */ static ssize_t HgfsRead(struct file *file, // IN: File to read from char __user *buf, // OUT: User buffer to copy data into size_t count, // IN: Number of bytes to read loff_t *offset) // IN: Offset at which to read { int result; ASSERT(file); ASSERT(file->f_dentry); ASSERT(buf); ASSERT(offset); LOG(6, (KERN_DEBUG "VMware hgfs: HgfsRead: read %Zu bytes from fh %u " "at offset %Lu\n", count, FILE_GET_FI_P(file)->handle, *offset)); result = HgfsRevalidate(file->f_dentry); if (result) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsRead: invalid dentry\n")); goto out; } result = generic_file_read(file, buf, count, offset); out: return result; } /* *---------------------------------------------------------------------- * * HgfsWrite -- * * Called whenever a process writes to a file in our filesystem. Our * function is just a thin wrapper around generic_write_file() that * tries to validate the dentry first. * * Note that files opened with O_SYNC (or superblocks mounted with * "sync") are synchronously written to by the VFS. * * Results: * Returns the number of bytes written on success, or an error on * failure. * * Side effects: * None * *---------------------------------------------------------------------- */ static ssize_t HgfsWrite(struct file *file, // IN: File to write to const char __user *buf, // IN: User buffer where the data is size_t count, // IN: Number of bytes to write loff_t *offset) // IN: Offset to begin writing at { int result; ASSERT(file); ASSERT(file->f_dentry); ASSERT(file->f_dentry->d_inode); ASSERT(buf); ASSERT(offset); LOG(6, (KERN_DEBUG "VMware hgfs: HgfsWrite: write %Zu bytes to fh %u " "at offset %Lu\n", count, FILE_GET_FI_P(file)->handle, *offset)); result = HgfsRevalidate(file->f_dentry); if (result) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsWrite: invalid dentry\n")); goto out; } result = generic_file_write(file, buf, count, offset); out: return result; } #endif /* *---------------------------------------------------------------------- * * HgfsFsync -- * * Called when user process calls fsync() on hgfs file. * * The hgfs protocol doesn't support fsync yet, so for now, we punt * and just return success. This is a little less sketchy than it * might sound, because hgfs skips the buffer cache in the guest * anyway (we always write to the host immediately). * * In the future we might want to try harder though, since * presumably the intent of an app calling fsync() is to get the * data onto persistent storage, and as things stand now we're at * the whim of the hgfs server code running on the host to fsync or * not if and when it pleases. * * Note that do_fsync will call filemap_fdatawrite() before us and * filemap_fdatawait() after us, so there's no need to do anything * here w.r.t. writing out dirty pages. * * Results: * Returns zero on success. (Currently always succeeds). * * Side effects: * None. * *---------------------------------------------------------------------- */ static int HgfsFsync(struct file *file, // IN: File we operate on struct dentry *dentry, // IN: Dentry for this file int datasync) // IN: fdatasync or fsync { LOG(6, (KERN_DEBUG "VMware hgfs: HgfsFsync: was called\n")); return 0; } /* *---------------------------------------------------------------------- * * HgfsMmap -- * * Called when user process calls mmap() on hgfs file. This is a very * thin wrapper function- we simply attempt to revalidate the * dentry prior to calling generic_file_mmap(). * * Results: * Returns zero on success. * Returns negative error value on failure * * Side effects: * None. * *---------------------------------------------------------------------- */ static int HgfsMmap(struct file *file, // IN: File we operate on struct vm_area_struct *vma) // IN/OUT: VM area information { int result; ASSERT(file); ASSERT(vma); ASSERT(file->f_dentry); LOG(6, (KERN_DEBUG "VMware hgfs: HgfsMmap: was called\n")); result = HgfsRevalidate(file->f_dentry); if (result) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsMmap: invalid dentry\n")); goto out; } result = generic_file_mmap(file, vma); out: return result; } /* *---------------------------------------------------------------------- * * HgfsRelease -- * * Called when the last user of a file closes it, i.e. when the * file's f_count becomes zero. * * Results: * Returns zero on success, or an error on failure. * * Side effects: * None * *---------------------------------------------------------------------- */ static int HgfsRelease(struct inode *inode, // IN: Inode that this file points to struct file *file) // IN: File that is getting released { HgfsSuperInfo *si; HgfsReq *req; HgfsHandle handle; HgfsOp opUsed; HgfsStatus replyStatus; int result = 0; ASSERT(inode); ASSERT(file); ASSERT(file->f_dentry); ASSERT(file->f_dentry->d_sb); handle = FILE_GET_FI_P(file)->handle; LOG(6, (KERN_DEBUG "VMware hgfs: HgfsRelease: close fh %u\n", handle)); /* * This may be our last open handle to an inode, so we should flush our * dirty pages before closing it. */ compat_filemap_write_and_wait(inode->i_mapping); HgfsReleaseFileInfo(file); si = HGFS_SB_TO_COMMON(file->f_dentry->d_sb); req = HgfsGetNewRequest(); if (!req) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsRelease: out of memory while " "getting new request\n")); result = -ENOMEM; goto out; } retry: opUsed = atomic_read(&hgfsVersionClose); if (opUsed == HGFS_OP_CLOSE_V3) { HgfsRequest *header; HgfsRequestCloseV3 *request; header = (HgfsRequest *)(HGFS_REQ_PAYLOAD(req)); header->id = req->id; header->op = opUsed; request = (HgfsRequestCloseV3 *)(HGFS_REQ_PAYLOAD_V3(req)); request->file = handle; request->reserved = 0; req->payloadSize = HGFS_REQ_PAYLOAD_SIZE_V3(request); } else { HgfsRequestClose *request; request = (HgfsRequestClose *)(HGFS_REQ_PAYLOAD(req)); request->header.id = req->id; request->header.op = opUsed; request->file = handle; req->payloadSize = sizeof *request; } /* Send the request and process the reply. */ result = HgfsSendRequest(req); if (result == 0) { /* Get the reply. */ replyStatus = HgfsReplyStatus(req); result = HgfsStatusConvertToLinux(replyStatus); switch (result) { case 0: LOG(4, (KERN_DEBUG "VMware hgfs: HgfsRelease: released handle %u\n", handle)); break; case -EPROTO: /* Retry with older version(s). Set globally. */ if (opUsed == HGFS_OP_CLOSE_V3) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsRelease: Version 3 not " "supported. Falling back to version 1.\n")); atomic_set(&hgfsVersionClose, HGFS_OP_CLOSE); goto retry; } break; default: LOG(4, (KERN_DEBUG "VMware hgfs: HgfsRelease: failed handle %u\n", handle)); break; } } else if (result == -EIO) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsRelease: timed out\n")); } else if (result == -EPROTO) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsRelease: server " "returned error: %d\n", result)); } else { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsRelease: unknown error: " "%d\n", result)); } out: HgfsFreeRequest(req); return result; } #ifndef VMW_SENDFILE_NONE /* *----------------------------------------------------------------------------- * * HgfsSendfile -- * * sendfile() wrapper for HGFS. Note that this is for sending a file * from HGFS to another filesystem (or socket). To use HGFS as the * destination file in a call to sendfile(), we must implement sendpage() * as well. * * Like mmap(), we're just interested in validating the dentry and then * calling into generic_file_sendfile(). * * Results: * Returns number of bytes written on success, or an error on failure. * * Side effects: * None. * *----------------------------------------------------------------------------- */ #if defined(VMW_SENDFILE_OLD) static ssize_t HgfsSendfile(struct file *file, // IN: File to read from loff_t *offset, // IN/OUT: Where to start reading size_t count, // IN: How much to read read_actor_t actor, // IN: Routine to send a page of data void __user *target) // IN: Destination file/socket #elif defined(VMW_SENDFILE_NEW) static ssize_t HgfsSendfile(struct file *file, // IN: File to read from loff_t *offset, // IN/OUT: Where to start reading size_t count, // IN: How much to read read_actor_t actor, // IN: Routine to send a page of data void *target) // IN: Destination file/socket #endif { ssize_t result; ASSERT(file); ASSERT(file->f_dentry); ASSERT(target); ASSERT(offset); ASSERT(actor); LOG(6, (KERN_DEBUG "VMware hgfs: HgfsSendfile: was called\n")); result = HgfsRevalidate(file->f_dentry); if (result) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsSendfile: invalid dentry\n")); goto out; } result = generic_file_sendfile (file, offset, count, actor, target); out: return result; } #endif #ifdef VMW_SPLICE_READ /* *----------------------------------------------------------------------------- * * HgfsSpliceRead -- * * splice_read() wrapper for HGFS. Note that this is for sending a file * from HGFS to another filesystem (or socket). To use HGFS as the * destination file in a call to splice, we must implement splice_write() * as well. * * Like mmap(), we're just interested in validating the dentry and then * calling into generic_file_splice_read(). * * Results: * Returns number of bytes written on success, or an error on failure. * * Side effects: * None. * *----------------------------------------------------------------------------- */ static ssize_t HgfsSpliceRead(struct file *file, // IN: File to read from loff_t *offset, // IN/OUT: Where to start reading struct pipe_inode_info *pipe, // IN: Pipe where to write data size_t len, // IN: How much to read unsigned int flags) // IN: Various flags { ssize_t result; ASSERT(file); ASSERT(file->f_dentry); LOG(6, (KERN_DEBUG "VMware hgfs: HgfsSpliceRead: was called\n")); result = HgfsRevalidate(file->f_dentry); if (result) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsSpliceRead: invalid dentry\n")); goto out; } result = generic_file_splice_read(file, offset, pipe, len, flags); out: return result; } #endif vmhgfs-only/filesystem.c0000444000000000000000000004723312025726746014377 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ /* * filesystem.c -- * * High-level filesystem operations for the filesystem portion of * the vmhgfs driver. */ /* Must come before any kernel header file. */ #include "driver-config.h" #include #include #include #include #include #include "compat_completion.h" #include "compat_dcache.h" #include "compat_fs.h" #include "compat_kernel.h" #include "compat_kthread.h" #include "compat_sched.h" #include "compat_semaphore.h" #include "compat_slab.h" #include "compat_spinlock.h" #include "compat_string.h" #include "compat_uaccess.h" #include "compat_version.h" /* Must be included after sched.h. */ #include #include "bdhandler.h" #include "filesystem.h" #include "hgfsDevLinux.h" #include "hgfsProto.h" #include "hgfsUtil.h" #include "module.h" #include "request.h" #include "fsutil.h" #include "vm_assert.h" #include "vm_basic_types.h" #include "rpcout.h" #include "hgfs.h" #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 25) #define KERNEL_25_FS 0 #else #define KERNEL_25_FS 1 #endif #define HGFS_BD_THREAD_NAME "VMware hgfs backdoor handler" /* Synchronization primitives. */ spinlock_t hgfsBigLock = SPIN_LOCK_UNLOCKED; long hgfsReqThreadFlags; wait_queue_head_t hgfsReqThreadWait; struct task_struct *hgfsReqThread; COMPAT_KTHREAD_DECLARE_STOP_INFO(); /* Other variables. */ compat_kmem_cache *hgfsReqCache = NULL; compat_kmem_cache *hgfsInodeCache = NULL; RpcOut *hgfsRpcOut = NULL; unsigned int hgfsIdCounter = 0; struct list_head hgfsReqsUnsent; /* Global protocol version switch. */ atomic_t hgfsVersionOpen; atomic_t hgfsVersionRead; atomic_t hgfsVersionWrite; atomic_t hgfsVersionClose; atomic_t hgfsVersionSearchOpen; atomic_t hgfsVersionSearchRead; atomic_t hgfsVersionSearchClose; atomic_t hgfsVersionGetattr; atomic_t hgfsVersionSetattr; atomic_t hgfsVersionCreateDir; atomic_t hgfsVersionDeleteFile; atomic_t hgfsVersionDeleteDir; atomic_t hgfsVersionRename; atomic_t hgfsVersionQueryVolumeInfo; atomic_t hgfsVersionCreateSymlink; /* Private functions. */ static inline unsigned long HgfsComputeBlockBits(unsigned long blockSize); static compat_kmem_cache_ctor HgfsInodeCacheCtor; static HgfsSuperInfo *HgfsInitSuperInfo(HgfsMountInfo *mountInfo); static int HgfsReadSuper(struct super_block *sb, void *rawData, int flags); /* HGFS filesystem high-level operations. */ #if KERNEL_25_FS /* { */ # if defined(VMW_GETSB_2618) static int HgfsGetSb(struct file_system_type *fs_type, int flags, const char *dev_name, void *rawData, struct vfsmount *mnt); # elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 70) static struct super_block *HgfsGetSb(struct file_system_type *fs_type, int flags, const char *dev_name, void *rawData); # else static struct super_block *HgfsGetSb(struct file_system_type *fs_type, int flags, char *dev_name, void *rawData); # endif #else /* } { */ static struct super_block *HgfsReadSuper24(struct super_block *sb, void *rawData, int flags); #endif /* } */ /* HGFS filesystem type structure. */ static struct file_system_type hgfsType = { .owner = THIS_MODULE, .name = HGFS_NAME, .fs_flags = FS_BINARY_MOUNTDATA, #if KERNEL_25_FS .get_sb = HgfsGetSb, .kill_sb = kill_anon_super, #else .read_super = HgfsReadSuper24, #endif }; /* * Private functions implementations. */ /* *----------------------------------------------------------------------------- * * HgfsComputeBlockBits -- * * Given a block size, returns the number of bits in the block, rounded * down. This approach of computing the number of bits per block and * saving it for later use is the same used in NFS. * * Results: * The number of bits in the block. * * Side effects: * None. * *----------------------------------------------------------------------------- */ static inline unsigned long HgfsComputeBlockBits(unsigned long blockSize) { uint8 numBits; for (numBits = 31; numBits && !(blockSize & (1 << numBits)); numBits--); return numBits; } /* *----------------------------------------------------------------------------- * * HgfsInodeCacheCtor -- * * Constructor for HGFS inode structures that runs once at slab * allocation. It is called once for each piece of memory that * is used to satisfy HGFS inode allocations; it should only be * used to initialize items that will naturally return to their * initialized state before deallocation (such as locks, list_heads). * * Results: * None. * * Side effects: * None. * *----------------------------------------------------------------------------- */ static void HgfsInodeCacheCtor(COMPAT_KMEM_CACHE_CTOR_ARGS(slabElem)) // IN: slab item to initialize { #ifdef VMW_EMBED_INODE HgfsInodeInfo *iinfo = (HgfsInodeInfo *)slabElem; /* * VFS usually calls this as part of allocating inodes for us, but since * we're doing the allocation now, we need to call it. It'll set up * much of the VFS inode members. */ inode_init_once(&iinfo->inode); #endif } /* *---------------------------------------------------------------------- * * HgfsInitSuperInfo -- * * Allocate and initialize a new HgfsSuperInfo object * * Results: * Returns a new HgfsSuperInfo object with all its fields initialized, * or an error code cast as a pointer. * * Side effects: * None * *---------------------------------------------------------------------- */ static HgfsSuperInfo * HgfsInitSuperInfo(HgfsMountInfo *mountInfo) // IN: Passed down from the user { HgfsSuperInfo *si = NULL; int result = 0; int len; char *tmpName; Bool hostValid; si = kmalloc(sizeof *si, GFP_KERNEL); if (!si) { result = -ENOMEM; goto out2; } /* * If the mounter specified a uid or gid, we will prefer them over any uid * or gid given to us by the server. */ si->uidSet = mountInfo->uidSet; if (si->uidSet) { si->uid = mountInfo->uid; } else { si->uid = current->uid; } si->gidSet = mountInfo->gidSet; if (si->gidSet) { si->gid = mountInfo->gid; } else { si->gid = current->gid; } si->fmask = mountInfo->fmask; si->dmask = mountInfo->dmask; si->ttl = mountInfo->ttl * HZ; // in ticks /* * We don't actually care about this field (though we may care in the * future). For now, just make sure it is set to ".host" as a sanity check. * * We can't call getname() directly because on certain kernels we can't call * putname() directly. For more details, see the change description of * change 464782 or the second comment in bug 159623, which fixed the same * problem for vmblock. */ tmpName = compat___getname(); if (!tmpName) { LOG(6, (KERN_DEBUG "VMware hgfs: HgfsInitSuperInfo: could not obtain " "memory for filename\n")); result = -ENOMEM; goto out2; } len = strncpy_from_user(tmpName, mountInfo->shareNameHost, PATH_MAX); if (len < 0 || len >= PATH_MAX) { LOG(6, (KERN_DEBUG "VMware hgfs: HgfsInitSuperInfo: strncpy_from_user " "on host string failed\n")); result = len < 0 ? len : -ENAMETOOLONG; goto out; } hostValid = strcmp(tmpName, ".host") == 0; if (!hostValid) { LOG(6, (KERN_DEBUG "VMware hgfs: HgfsInitSuperInfo: host string is " "invalid\n")); result = -EINVAL; goto out; } /* * Perform a simple sanity check on the directory portion: it must begin * with forward slash. */ len = strncpy_from_user(tmpName, mountInfo->shareNameDir, PATH_MAX); if (len < 0 || len >= PATH_MAX) { LOG(6, (KERN_DEBUG "VMware hgfs: HgfsInitSuperInfo: strncpy_from_user " "on dir string failed\n")); result = len < 0 ? len : -ENAMETOOLONG; goto out; } if (*tmpName != '/') { LOG(6, (KERN_DEBUG "VMware hgfs: HgfsInitSuperInfo: dir string is " "invalid\n")); result = -EINVAL; goto out; } /* * The SELinux audit subsystem will delay the putname() of a string until * the end of a system call so that it may be audited at any point. At that * time, it also unconditionally calls putname() on every string allocated * by getname(). * * This means we can't safely retain strings allocated by getname() beyond * the syscall boundary. So after getting the string, use kstrdup() to * duplicate it, and store that (audit-safe) result in the SuperInfo struct. */ si->shareName = compat_kstrdup(tmpName, GFP_KERNEL); if (si->shareName == NULL) { LOG(6, (KERN_DEBUG "VMware hgfs: HgfsInitSuperInfo: kstrdup on " "dir string failed\n")); result = -ENOMEM; goto out; } si->shareNameLen = strlen(si->shareName); out: compat___putname(tmpName); out2: if (result) { /* If we failed, si->shareName couldn't have been allocated. */ kfree(si); si = ERR_PTR(result); } return si; } /* *----------------------------------------------------------------------------- * * HgfsReadSuper -- * * The main entry point of the filesystem side of the driver. Called when * a userland process does a mount(2) of an hgfs filesystem. This makes the * whole driver transition from its initial state to state 1. Fill the * content of the uninitialized superblock provided by the kernel. * * 'rawData' is a pointer (that can be NULL) to a kernel buffer (whose * size is <= PAGE_SIZE) that corresponds to the filesystem-specific 'data' * argument passed to mount(2). * * Results: * zero and initialized superblock on success * negative value on failure * * Side effects: * None * *----------------------------------------------------------------------------- */ static int HgfsReadSuper(struct super_block *sb, // OUT: Superblock object void *rawData, // IN: Fs-specific mount data int flags) // IN: Mount flags { int result; HgfsSuperInfo *si; HgfsMountInfo *mountInfo; struct dentry *rootDentry; ASSERT(sb); LOG(6, (KERN_DEBUG "VMware hgfs: HgfsReadSuper: entered\n")); /* Sanity check the incoming user data. */ mountInfo = (HgfsMountInfo *)rawData; if (!mountInfo || mountInfo->magicNumber != HGFS_SUPER_MAGIC || mountInfo->version != HGFS_PROTOCOL_VERSION) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsReadSuper: bad mount data passed " "in by user, failing!\n")); return -EINVAL; } /* Setup both our superblock and the VFS superblock. */ si = HgfsInitSuperInfo(mountInfo); if (IS_ERR(si)) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsReadSuper: superinfo " "init failed\n")); return PTR_ERR(si); } HGFS_SET_SB_TO_COMMON(sb, si); sb->s_magic = HGFS_SUPER_MAGIC; sb->s_op = &HgfsSuperOperations; /* * If s_maxbytes isn't initialized, the generic write path may fail. In * most kernels, s_maxbytes is initialized by the kernel's superblock * allocation routines, but in some, it's up to the filesystem to initialize * it. Note that we'll initialize it anyway, because the default value is * MAX_NON_LFS, which caps our filesize at 2^32 bytes. */ #ifdef VMW_SB_HAS_MAXBYTES sb->s_maxbytes = MAX_LFS_FILESIZE; #endif /* * These two operations will make sure that our block size and the bits * per block match up, no matter what HGFS_BLOCKSIZE may be. Granted, * HGFS_BLOCKSIZE will always be a power of two, but you never know! */ sb->s_blocksize_bits = HgfsComputeBlockBits(HGFS_BLOCKSIZE); sb->s_blocksize = 1 << sb->s_blocksize_bits; /* * We can't use d_alloc_root() here directly because it requires a valid * inode, which only HgfsInstantiate will create. So instead, we'll do the * work in pieces. First we'll allocate the dentry and setup its parent * and superblock. Then HgfsInstantiate will do the rest, issuing a getattr, * getting the inode, and instantiating the dentry with it. */ rootDentry = compat_d_alloc_name(NULL, "/"); if (rootDentry == NULL) { LOG(4, (KERN_WARNING "VMware hgfs: HgfsReadSuper: Could not allocate " "root dentry\n")); result = -ENOMEM; goto exit; } rootDentry->d_parent = rootDentry; rootDentry->d_sb = sb; result = HgfsInstantiate(rootDentry, HGFS_ROOT_INO, NULL); if (result) { LOG(4, (KERN_WARNING "VMware hgfs: HgfsReadSuper: Could not instantiate " "root dentry\n")); goto exit; } sb->s_root = rootDentry; LOG(6, (KERN_DEBUG "VMware hgfs: HgfsReadSuper: finished %s\n", si->shareName)); exit: if (result) { dput(rootDentry); kfree(si->shareName); kfree(si); } return result; } /* * HGFS filesystem high-level operations. */ #if KERNEL_25_FS #if defined(VMW_GETSB_2618) /* *----------------------------------------------------------------------------- * * HgfsGetSb -- * * Invokes generic kernel code to prepare superblock for * deviceless filesystem. * * Results: * 0 on success * non-zero on failure * * Side effects: * None * *----------------------------------------------------------------------------- */ static int HgfsGetSb(struct file_system_type *fs_type, int flags, const char *dev_name, void *rawData, struct vfsmount *mnt) { return get_sb_nodev(fs_type, flags, rawData, HgfsReadSuper, mnt); } #else /* *----------------------------------------------------------------------------- * * HgfsGetSb -- * * Invokes generic kernel code to prepare superblock for * deviceless filesystem. * * Results: * The initialized superblock on success * NULL on failure * * Side effects: * None * *----------------------------------------------------------------------------- */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 70) static struct super_block * HgfsGetSb(struct file_system_type *fs_type, int flags, const char *dev_name, void *rawData) #else static struct super_block * HgfsGetSb(struct file_system_type *fs_type, int flags, char *dev_name, void *rawData) #endif { return get_sb_nodev(fs_type, flags, rawData, HgfsReadSuper); } #endif #else /* *----------------------------------------------------------------------------- * * HgfsReadSuper24 -- * * Compatibility wrapper for 2.4.x kernels read_super. * Converts success to sb, and failure to NULL. * * Results: * The initialized superblock on success * NULL on failure * * Side effects: * None * *----------------------------------------------------------------------------- */ static struct super_block * HgfsReadSuper24(struct super_block *sb, void *rawData, int flags) { return HgfsReadSuper(sb, rawData, flags) ? NULL : sb; } #endif /* * Public function implementations. */ /* *----------------------------------------------------------------------------- * * HgfsInitFileSystem -- * * Initializes the file system and registers it with the kernel. * * Results: * TRUE on success, FALSE on failure. * * Side effects: * None * *----------------------------------------------------------------------------- */ Bool HgfsInitFileSystem(void) { Bool success = FALSE; /* Initialize primitives. */ INIT_LIST_HEAD(&hgfsReqsUnsent); init_waitqueue_head(&hgfsReqThreadWait); hgfsReqThread = NULL; hgfsReqThreadFlags = 0; HgfsResetOps(); /* Setup the request slab allocator. */ hgfsReqCache = compat_kmem_cache_create("hgfsReqCache", sizeof (HgfsReq), 0, SLAB_HWCACHE_ALIGN, NULL); if (hgfsReqCache == NULL) { printk(KERN_WARNING "VMware hgfs: failed to create request allocator\n"); goto exit; } /* Setup the inode slab allocator. */ hgfsInodeCache = compat_kmem_cache_create("hgfsInodeCache", sizeof (HgfsInodeInfo), 0, SLAB_HWCACHE_ALIGN, HgfsInodeCacheCtor); if (hgfsInodeCache == NULL) { printk(KERN_WARNING "VMware hgfs: failed to create inode allocator\n"); goto exit; } /* Create backdoor handler. */ hgfsReqThread = compat_kthread_run(HgfsBdHandler, NULL, HGFS_NAME); if (IS_ERR(hgfsReqThread)) { printk(KERN_WARNING "VMware hgfs: failed to create kernel thread\n"); goto exit; } /* * Register the filesystem. This should be the last thing we do * in init_module. */ if (register_filesystem(&hgfsType)) { printk(KERN_WARNING "VMware hgfs: failed to register filesystem\n"); goto exit; } LOG(4, (KERN_DEBUG "VMware hgfs: Module Loaded\n")); #ifdef HGFS_ENABLE_WRITEBACK LOG(4, (KERN_DEBUG "VMware hgfs: writeback cache enabled\n")); #endif success = TRUE; exit: /* Cleanup if an error occurred. */ if (success == FALSE) { if (!IS_ERR(hgfsReqThread)) { compat_kthread_stop(hgfsReqThread); } if (hgfsInodeCache != NULL) { kmem_cache_destroy(hgfsInodeCache); } if (hgfsReqCache != NULL) { kmem_cache_destroy(hgfsReqCache); } } return success; } /* *----------------------------------------------------------------------------- * * HgfsCleanupFileSystem -- * * Cleans up file system and unregisters it with the kernel. * * Results: * TRUE on success, FALSE on failure. * * Side effects: * None. * *----------------------------------------------------------------------------- */ Bool HgfsCleanupFileSystem(void) { Bool success = TRUE; /* FIXME: Check actual kernel version when RR's modules went in */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 45) if (MOD_IN_USE) { printk(KERN_WARNING "VMware hgfs: filesystem in use, removal failed\n"); success = FALSE; } #endif /* * Unregister the filesystem. This should be the first thing we do in * the module cleanup code. */ if (unregister_filesystem(&hgfsType)) { printk(KERN_WARNING "VMware hgfs: failed to unregister filesystem\n"); success = FALSE; } /* Kill the backdoor handler thread. */ compat_kthread_stop(hgfsReqThread); /* Destroy the inode and request slabs. */ kmem_cache_destroy(hgfsInodeCache); kmem_cache_destroy(hgfsReqCache); LOG(4, (KERN_DEBUG "VMware hgfs: Module Unloaded\n")); return success; } vmhgfs-only/filesystem.h0000444000000000000000000000232112025726746014371 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ /* * filesystem.h -- * * High-level filesystem operations for the filesystem portion of * the vmhgfs driver. */ #ifndef _HGFS_DRIVER_FILESYSTEM_H_ #define _HGFS_DRIVER_FILESYSTEM_H_ #include "vm_basic_types.h" /* Public functions (with respect to the entire module). */ Bool HgfsInitFileSystem(void); Bool HgfsCleanupFileSystem(void); #endif // _HGFS_DRIVER_FILESYSTEM_H_ vmhgfs-only/fsutil.c0000444000000000000000000015766312025726746013532 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ /* * fsutil.c -- * * Functions used in more than one type of filesystem operation will be * exported from this file. */ /* Must come before any kernel header file. */ #include "driver-config.h" #include /* Must come before compat_dcache. */ #include "compat_fs.h" #include "compat_dcache.h" #include "compat_kernel.h" #include "compat_sched.h" #include "compat_slab.h" #include "compat_spinlock.h" #include "escBitvector.h" #include "cpName.h" #include "cpNameLite.h" #include "hgfsUtil.h" #include "module.h" #include "request.h" #include "fsutil.h" #include "hgfsProto.h" #include "staticEscape.h" #include "vm_assert.h" #include "vm_basic_types.h" static struct inode *HgfsInodeLookup(struct super_block *sb, ino_t ino); static void HgfsSetFileType(struct inode *inode, HgfsAttrInfo const *attr); static int HgfsUnpackGetattrReply(HgfsReq *req, HgfsAttrInfo *attr); static int HgfsPackGetattrRequest(HgfsReq *req, struct dentry *dentry, Bool allowHandleReuse, HgfsOp opUsed, HgfsAttrInfo *attr); /* * Private function implementations. */ /* *---------------------------------------------------------------------- * * HgfsInodeLookup -- * * The equivalent of ilookup() in the Linux kernel. We have an HGFS * specific implementation in order to hack around the lack of * ilookup() on older kernels. * * Results: * Pointer to the VFS inode using the current inode number if it * already exists in the inode cache, NULL otherwise. * * Side effects: * None * *---------------------------------------------------------------------- */ static struct inode * HgfsInodeLookup(struct super_block *sb, // IN: Superblock of this fs ino_t ino) // IN: Inode number to look up { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 42) return ilookup(sb, ino); #else struct inode *inode; HgfsInodeInfo *iinfo; /* * Note that returning NULL in both of these cases will make the * caller think that no such inode exists, which is correct. In the first * case, we failed to allocate an inode inside iget(), meaning the inode * number didn't already exist in the inode cache. In the second case, the * inode got marked bad inside read_inode, also indicative of a new inode * allocation. */ inode = HgfsGetInode(sb, ino); if (inode == NULL) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsInodeLookup: iget ran out of " "memory and returned NULL\n")); return NULL; } if (is_bad_inode(inode)) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsInodeLookup: inode marked bad\n")); goto iput_and_exit; } /* * Our read_inode function should guarantee that if we're here, iinfo should * have been allocated already. */ iinfo = INODE_GET_II_P(inode); ASSERT(iinfo); if (iinfo == NULL) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsInodeLookup: found corrupt inode, " "bailing out\n")); goto iput_and_exit; } /* * It's HGFS's job to make sure this is set to TRUE in all inodes on which * we hold a reference. If it is set to TRUE, we return the inode, just as * ilookup() does. * * XXX: Note that there exists a race here and in HgfsIget (between the time * that the inode is unlocked and isReferencedInode is set), but I'm hoping * that it doesn't matter because anyone executing this code can't posibly * be "CONFIG_PREEMPT=y". */ if (iinfo->isReferencedInode) { goto exit; } iput_and_exit: iput(inode); inode = NULL; exit: return inode; #endif } /* *---------------------------------------------------------------------- * * HgfsSetFileType -- * * Set file type in inode according to the hgfs attributes. * * Results: * None * * Side effects: * None * *---------------------------------------------------------------------- */ static void HgfsSetFileType(struct inode *inode, // IN/OUT: Inode to update HgfsAttrInfo const *attr) // IN: Attrs to use to update { ASSERT(inode); ASSERT(attr); switch (attr->type) { case HGFS_FILE_TYPE_DIRECTORY: inode->i_mode = S_IFDIR; inode->i_op = &HgfsDirInodeOperations; inode->i_fop = &HgfsDirFileOperations; break; case HGFS_FILE_TYPE_SYMLINK: inode->i_mode = S_IFLNK; inode->i_op = &HgfsLinkInodeOperations; break; case HGFS_FILE_TYPE_REGULAR: inode->i_mode = S_IFREG; inode->i_op = &HgfsFileInodeOperations; inode->i_fop = &HgfsFileFileOperations; inode->i_data.a_ops = &HgfsAddressSpaceOperations; break; default: /* * XXX Should never happen. I'd put NOT_IMPLEMENTED() here * but if the driver ever goes in the host it's probably not * a good idea for an attacker to be able to hang the host * simply by using a bogus file type in a reply. [bac] */ LOG(4, (KERN_DEBUG "VMware hgfs: HgfsSetFileType: UNSUPPORTED " "inode type\n")); inode->i_mode = 0; // NOT_IMPLEMENTED(); break; } } /* *---------------------------------------------------------------------- * * HgfsUnpackGetattrReply -- * * This function abstracts the differences between a GetattrV1 and * a GetattrV2. The caller provides the packet containing the reply * and we populate the AttrInfo with version-independent information. * * Note that attr->requestType has already been populated so that we * know whether to expect a V1 or V2 reply. * * Results: * 0 on success, anything else on failure. * * Side effects: * None * *---------------------------------------------------------------------- */ static int HgfsUnpackGetattrReply(HgfsReq *req, // IN: Reply packet HgfsAttrInfo *attr) // IN/OUT: Attributes { int result; char *name = NULL; uint32 length = 0; ASSERT(req); ASSERT(attr); result = HgfsUnpackCommonAttr(req, attr); if (result != 0) { return result; } /* GetattrV2+ also wants a symlink target if it exists. */ if (attr->requestType == HGFS_OP_GETATTR_V3) { HgfsReplyGetattrV3 *replyV3 = (HgfsReplyGetattrV3 *)(HGFS_REP_PAYLOAD_V3(req)); name = replyV3->symlinkTarget.name; length = replyV3->symlinkTarget.length; /* Skip the symlinkTarget if it's too long. */ if (length > HGFS_NAME_BUFFER_SIZET(sizeof *replyV3 + sizeof(HgfsReply))) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsUnpackGetattrReply: symlink " "target name too long, ignoring\n")); return -ENAMETOOLONG; } } else if (attr->requestType == HGFS_OP_GETATTR_V2) { HgfsReplyGetattrV2 *replyV2 = (HgfsReplyGetattrV2 *) (HGFS_REQ_PAYLOAD(req)); name = replyV2->symlinkTarget.name; length = replyV2->symlinkTarget.length; /* Skip the symlinkTarget if it's too long. */ if (length > HGFS_NAME_BUFFER_SIZE(replyV2)) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsUnpackGetattrReply: symlink " "target name too long, ignoring\n")); return -ENAMETOOLONG; } } if (length != 0) { attr->fileName = kmalloc(length + 1, GFP_KERNEL); if (attr->fileName == NULL) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsUnpackGetattrReply: out of " "memory allocating symlink target name, ignoring\n")); return -ENOMEM; } /* Copy and convert. From now on, the symlink target is in UTF8. */ memcpy(attr->fileName, name, length); CPNameLite_ConvertFrom(attr->fileName, length, '/'); attr->fileName[length] = '\0'; } return 0; } /* *---------------------------------------------------------------------- * * HgfsPackGetattrRequest -- * * Setup the getattr request, depending on the op version. When possible, * we will issue the getattr using an existing open HGFS handle. * * Results: * Returns zero on success, or negative error on failure. * * Side effects: * None * *---------------------------------------------------------------------- */ static int HgfsPackGetattrRequest(HgfsReq *req, // IN/OUT: Request buffer struct dentry *dentry, // IN: Dentry containing name Bool allowHandleReuse, // IN: Can we use a handle? HgfsOp opUsed, // IN: Op to be used HgfsAttrInfo *attr) // OUT: Attrs to update { size_t reqBufferSize; size_t reqSize; int result = 0; HgfsHandle handle; char *fileName = NULL; uint32 *fileNameLength = NULL; ASSERT(attr); ASSERT(dentry); ASSERT(req); attr->requestType = opUsed; switch (opUsed) { case HGFS_OP_GETATTR_V3: { HgfsRequest *requestHeader; HgfsRequestGetattrV3 *requestV3; /* Fill out the request packet. */ requestHeader = (HgfsRequest *)(HGFS_REQ_PAYLOAD(req)); requestHeader->op = opUsed; requestHeader->id = req->id; requestV3 = (HgfsRequestGetattrV3 *)HGFS_REQ_PAYLOAD_V3(req); /* * When possible, issue a getattr using an existing handle. This will * give us slightly better performance on a Windows server, and is more * correct regardless. If we don't find a handle, fall back on getattr * by name. */ requestV3->hints = 0; if (allowHandleReuse && HgfsGetHandle(dentry->d_inode, 0, &handle) == 0) { requestV3->fileName.flags = HGFS_FILE_NAME_USE_FILE_DESC; requestV3->fileName.fid = handle; requestV3->fileName.length = 0; requestV3->fileName.caseType = HGFS_FILE_NAME_DEFAULT_CASE; fileName = NULL; } else { fileName = requestV3->fileName.name; fileNameLength = &requestV3->fileName.length; requestV3->fileName.flags = 0; requestV3->fileName.fid = HGFS_INVALID_HANDLE; requestV3->fileName.caseType = HGFS_FILE_NAME_CASE_SENSITIVE; } requestV3->reserved = 0; reqSize = HGFS_REQ_PAYLOAD_SIZE_V3(requestV3); reqBufferSize = HGFS_NAME_BUFFER_SIZET(reqSize); break; } case HGFS_OP_GETATTR_V2: { HgfsRequestGetattrV2 *requestV2; requestV2 = (HgfsRequestGetattrV2 *)(HGFS_REQ_PAYLOAD(req)); requestV2->header.op = opUsed; requestV2->header.id = req->id; /* * When possible, issue a getattr using an existing handle. This will * give us slightly better performance on a Windows server, and is more * correct regardless. If we don't find a handle, fall back on getattr * by name. */ if (allowHandleReuse && HgfsGetHandle(dentry->d_inode, 0, &handle) == 0) { requestV2->hints = HGFS_ATTR_HINT_USE_FILE_DESC; requestV2->file = handle; fileName = NULL; } else { requestV2->hints = 0; fileName = requestV2->fileName.name; fileNameLength = &requestV2->fileName.length; } reqSize = sizeof *requestV2; reqBufferSize = HGFS_NAME_BUFFER_SIZE(requestV2); break; } case HGFS_OP_GETATTR: { HgfsRequestGetattr *requestV1; requestV1 = (HgfsRequestGetattr *)(HGFS_REQ_PAYLOAD(req)); requestV1->header.op = opUsed; requestV1->header.id = req->id; fileName = requestV1->fileName.name; fileNameLength = &requestV1->fileName.length; reqSize = sizeof *requestV1; reqBufferSize = HGFS_NAME_BUFFER_SIZE(requestV1); break; } default: LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPackGetattrRequest: unexpected " "OP type encountered\n")); result = -EPROTO; goto out; } /* Avoid all this extra work when we're doing a getattr by handle. */ if (fileName != NULL) { /* Build full name to send to server. */ if (HgfsBuildPath(fileName, reqBufferSize, dentry) < 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPackGetattrRequest: build path " "failed\n")); result = -EINVAL; goto out; } LOG(6, (KERN_DEBUG "VMware hgfs: HgfsPackGetattrRequest: getting attrs " "for \"%s\"\n", fileName)); /* Convert to CP name. */ result = CPName_ConvertTo(fileName, reqBufferSize, fileName); if (result < 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPackGetattrRequest: CP " "conversion failed\n")); result = -EINVAL; goto out; } /* Unescape the CP name. */ result = HgfsUnescapeBuffer(fileName, result); *fileNameLength = result; } req->payloadSize = reqSize + result; result = 0; out: return result; } /* * Public function implementations. */ /* *---------------------------------------------------------------------- * * HgfsUnpackCommonAttr -- * * This function abstracts the HgfsAttr struct behind HgfsAttrInfo. * Callers can pass one of four replies into it and receive back the * attributes for those replies. * * Callers must populate attr->requestType so that we know whether to * expect a V1 or V2 Attr struct. * * Results: * Zero on success, non-zero otherwise. * * Side effects: * None * *---------------------------------------------------------------------- */ int HgfsUnpackCommonAttr(HgfsReq *req, // IN: Reply packet HgfsAttrInfo *attrInfo) // OUT: Attributes { HgfsReplyGetattrV3 *getattrReplyV3; HgfsReplyGetattrV2 *getattrReplyV2; HgfsReplyGetattr *getattrReplyV1; HgfsReplySearchReadV3 *searchReadReplyV3; HgfsReplySearchReadV2 *searchReadReplyV2; HgfsReplySearchRead *searchReadReplyV1; HgfsDirEntry *dirent; HgfsAttrV2 *attrV2 = NULL; HgfsAttr *attrV1 = NULL; ASSERT(req); ASSERT(attrInfo); switch (attrInfo->requestType) { case HGFS_OP_GETATTR_V3: getattrReplyV3 = (HgfsReplyGetattrV3 *)(HGFS_REP_PAYLOAD_V3(req)); attrV2 = &getattrReplyV3->attr; break; case HGFS_OP_GETATTR_V2: getattrReplyV2 = (HgfsReplyGetattrV2 *)(HGFS_REQ_PAYLOAD(req)); attrV2 = &getattrReplyV2->attr; break; case HGFS_OP_GETATTR: getattrReplyV1 = (HgfsReplyGetattr *)(HGFS_REQ_PAYLOAD(req)); attrV1 = &getattrReplyV1->attr; break; case HGFS_OP_SEARCH_READ_V3: searchReadReplyV3 = (HgfsReplySearchReadV3 *)(HGFS_REP_PAYLOAD_V3(req)); dirent = (HgfsDirEntry *)searchReadReplyV3->payload; attrV2 = &dirent->attr; break; case HGFS_OP_SEARCH_READ_V2: searchReadReplyV2 = (HgfsReplySearchReadV2 *)(HGFS_REQ_PAYLOAD(req)); attrV2 = &searchReadReplyV2->attr; break; case HGFS_OP_SEARCH_READ: searchReadReplyV1 = (HgfsReplySearchRead *)(HGFS_REQ_PAYLOAD(req)); attrV1 = &searchReadReplyV1->attr; break; default: LOG(4, (KERN_DEBUG "VMware hgfs: HgfsUnpackCommonAttr: unexpected op " "in reply packet\n")); return -EPROTO; } if (attrV2 != NULL) { attrInfo->mask = 0; if (attrV2->mask & HGFS_ATTR_VALID_TYPE) { attrInfo->type = attrV2->type; attrInfo->mask |= HGFS_ATTR_VALID_TYPE; } if (attrV2->mask & HGFS_ATTR_VALID_SIZE) { attrInfo->size = attrV2->size; attrInfo->mask |= HGFS_ATTR_VALID_SIZE; } if (attrV2->mask & HGFS_ATTR_VALID_ACCESS_TIME) { attrInfo->accessTime = attrV2->accessTime; attrInfo->mask |= HGFS_ATTR_VALID_ACCESS_TIME; } if (attrV2->mask & HGFS_ATTR_VALID_WRITE_TIME) { attrInfo->writeTime = attrV2->writeTime; attrInfo->mask |= HGFS_ATTR_VALID_WRITE_TIME; } if (attrV2->mask & HGFS_ATTR_VALID_CHANGE_TIME) { attrInfo->attrChangeTime = attrV2->attrChangeTime; attrInfo->mask |= HGFS_ATTR_VALID_CHANGE_TIME; } if (attrV2->mask & HGFS_ATTR_VALID_SPECIAL_PERMS) { attrInfo->specialPerms = attrV2->specialPerms; attrInfo->mask |= HGFS_ATTR_VALID_SPECIAL_PERMS; } if (attrV2->mask & HGFS_ATTR_VALID_OWNER_PERMS) { attrInfo->ownerPerms = attrV2->ownerPerms; attrInfo->mask |= HGFS_ATTR_VALID_OWNER_PERMS; } if (attrV2->mask & HGFS_ATTR_VALID_GROUP_PERMS) { attrInfo->groupPerms = attrV2->groupPerms; attrInfo->mask |= HGFS_ATTR_VALID_GROUP_PERMS; } if (attrV2->mask & HGFS_ATTR_VALID_OTHER_PERMS) { attrInfo->otherPerms = attrV2->otherPerms; attrInfo->mask |= HGFS_ATTR_VALID_OTHER_PERMS; } if (attrV2->mask & HGFS_ATTR_VALID_USERID) { attrInfo->userId = attrV2->userId; attrInfo->mask |= HGFS_ATTR_VALID_USERID; } if (attrV2->mask & HGFS_ATTR_VALID_GROUPID) { attrInfo->groupId = attrV2->groupId; attrInfo->mask |= HGFS_ATTR_VALID_GROUPID; } if (attrV2->mask & HGFS_ATTR_VALID_FILEID) { attrInfo->hostFileId = attrV2->hostFileId; attrInfo->mask |= HGFS_ATTR_VALID_FILEID; } } else if (attrV1 != NULL) { /* Implicit mask for a Version 1 attr. */ attrInfo->mask = HGFS_ATTR_VALID_TYPE | HGFS_ATTR_VALID_SIZE | HGFS_ATTR_VALID_ACCESS_TIME | HGFS_ATTR_VALID_WRITE_TIME | HGFS_ATTR_VALID_CHANGE_TIME | HGFS_ATTR_VALID_OWNER_PERMS; attrInfo->type = attrV1->type; attrInfo->size = attrV1->size; attrInfo->accessTime = attrV1->accessTime; attrInfo->writeTime = attrV1->writeTime; attrInfo->attrChangeTime = attrV1->attrChangeTime; attrInfo->ownerPerms = attrV1->permissions; } return 0; } /* *----------------------------------------------------------------------------- * * HgfsEscapeBuffer -- * * Escape any characters that are not legal in a linux filename, * which is just the character "/". We also of course have to * escape the escape character, which is "%". * * sizeBufOut must account for the NUL terminator. * * XXX: See the comments in staticEscape.c and staticEscapeW.c to understand * why this interface sucks. * * Results: * On success, the size (excluding the NUL terminator) of the * escaped, NUL terminated buffer. * On failure (bufOut not big enough to hold result), negative value. * * Side effects: * None * *----------------------------------------------------------------------------- */ int HgfsEscapeBuffer(char const *bufIn, // IN: Buffer with unescaped input uint32 sizeIn, // IN: Size of input buffer (chars) uint32 sizeBufOut, // IN: Size of output buffer (bytes) char *bufOut) // OUT: Buffer for escaped output { /* * This is just a wrapper around the more general escape * routine; we pass it the correct bitvector and the * buffer to escape. [bac] */ EscBitVector bytesToEsc; ASSERT(bufIn); ASSERT(bufOut); /* Set up the bitvector for "/" and "%" */ EscBitVector_Init(&bytesToEsc); EscBitVector_Set(&bytesToEsc, (unsigned char)'%'); EscBitVector_Set(&bytesToEsc, (unsigned char)'/'); return StaticEscape_Do('%', &bytesToEsc, bufIn, sizeIn, sizeBufOut, bufOut); } /* *----------------------------------------------------------------------------- * * HgfsUnescapeBuffer -- * * Unescape a buffer that was escaped using HgfsEscapeBuffer. * * The unescaping is done in place in the input buffer, and * can not fail. * * Results: * The size (excluding the NUL terminator) of the unescaped, NUL * terminated buffer. * * Side effects: * None * *----------------------------------------------------------------------------- */ int HgfsUnescapeBuffer(char *bufIn, // IN: Buffer to be unescaped uint32 sizeIn) // IN: Size of input buffer { /* * This is just a wrapper around the more general unescape * routine; we pass it the correct escape character and the * buffer to unescape. [bac] */ ASSERT(bufIn); return StaticEscape_Undo('%', bufIn, sizeIn); } /* *---------------------------------------------------------------------- * * HgfsChangeFileAttributes -- * * Update an inode's attributes to match those of the HgfsAttr. May * cause dirty pages to be flushed, and may invalidate cached pages, * if there was a change in the file size or modification time in * the server. * * Results: * None * * Side effects: * None * *---------------------------------------------------------------------- */ void HgfsChangeFileAttributes(struct inode *inode, // IN/OUT: Inode HgfsAttrInfo const *attr) // IN: New attrs { HgfsSuperInfo *si; Bool needInvalidate = FALSE; ASSERT(inode); ASSERT(inode->i_sb); ASSERT(attr); si = HGFS_SB_TO_COMMON(inode->i_sb); LOG(6, (KERN_DEBUG "VMware hgfs: HgfsChangeFileAttributes: entered\n")); HgfsSetFileType(inode, attr); /* * Set the access mode. For hosts that don't give us group or other * bits (Windows), we use the owner bits in their stead. */ inode->i_mode &= ~S_IALLUGO; if (attr->mask & HGFS_ATTR_VALID_SPECIAL_PERMS) { inode->i_mode |= (attr->specialPerms << 9); } if (attr->mask & HGFS_ATTR_VALID_OWNER_PERMS) { inode->i_mode |= (attr->ownerPerms << 6); } if (attr->mask & HGFS_ATTR_VALID_GROUP_PERMS) { inode->i_mode |= (attr->groupPerms << 3); } else { inode->i_mode |= ((inode->i_mode & S_IRWXU) >> 3); } if (attr->mask & HGFS_ATTR_VALID_OTHER_PERMS) { inode->i_mode |= (attr->otherPerms); } else { inode->i_mode |= ((inode->i_mode & S_IRWXU) >> 6); } /* Mask the access mode. */ switch (attr->type) { case HGFS_FILE_TYPE_REGULAR: inode->i_mode &= ~si->fmask; break; case HGFS_FILE_TYPE_DIRECTORY: inode->i_mode &= ~si->dmask; break; default: /* Nothing else gets masked. */ break; } /* * This field is used to represent the number of hard links. If the file is * really a file, this is easy; our filesystem doesn't support hard-linking, * so we just set it to 1. If the field is a directory, the number of links * represents the number of subdirectories, including '.' and "..". * * In either case, what we're doing isn't ideal. We've carefully tracked the * number of links through calls to HgfsMkdir and HgfsDelete, and now some * revalidate will make us trample on the number of links. But we have no * choice: someone on the server may have made our local view of the number * of links inconsistent (by, say, removing a directory) , and without the * ability to retrieve nlink via getattr, we have no way of knowing that. * * XXX: So in the future, adding nlink to getattr would be nice. At that * point we may as well just implement hard links anyway. Note that user * programs seem to have issues with a link count greater than 1 that isn't * accurate. I experimented with setting nlink to 2 for directories (to * account for '.' and ".."), and find printed a hard link error. So until * we have getattr support for nlink, everyone gets 1. */ inode->i_nlink = 1; /* * Use the stored uid and gid if we were given them at mount-time, or if * the server didn't give us a uid or gid. */ if (si->uidSet || (attr->mask & HGFS_ATTR_VALID_USERID) == 0) { inode->i_uid = si->uid; } else { inode->i_uid = attr->userId; } if (si->gidSet || (attr->mask & HGFS_ATTR_VALID_GROUPID) == 0) { inode->i_gid = si->gid; } else { inode->i_gid = attr->groupId; } inode->i_rdev = 0; /* Device nodes are not supported */ #if !defined(VMW_INODE_2618) inode->i_blksize = HGFS_BLOCKSIZE; #endif /* * Invalidate cached pages if we didn't receive the file size, or if it has * changed on the server. */ if (attr->mask & HGFS_ATTR_VALID_SIZE) { loff_t oldSize = compat_i_size_read(inode); inode->i_blocks = (attr->size + HGFS_BLOCKSIZE - 1) / HGFS_BLOCKSIZE; if (oldSize != attr->size) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsChangeFileAttributes: new file " "size: %"FMT64"u, old file size: %Lu\n", attr->size, oldSize)); needInvalidate = TRUE; } compat_i_size_write(inode, attr->size); } else { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsChangeFileAttributes: did not " "get file size\n")); needInvalidate = TRUE; } if (attr->mask & HGFS_ATTR_VALID_ACCESS_TIME) { HGFS_SET_TIME(inode->i_atime, attr->accessTime); } else { HGFS_SET_TIME(inode->i_atime, HGFS_GET_CURRENT_TIME()); } /* * Invalidate cached pages if we didn't receive the modification time, or if * it has changed on the server. */ if (attr->mask & HGFS_ATTR_VALID_WRITE_TIME) { HGFS_DECLARE_TIME(newTime); HGFS_SET_TIME(newTime, attr->writeTime); if (!HGFS_EQUAL_TIME(newTime, inode->i_mtime)) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsChangeFileAttributes: new mod " "time: %ld:%lu, old mod time: %ld:%lu\n", HGFS_PRINT_TIME(newTime), HGFS_PRINT_TIME(inode->i_mtime))); needInvalidate = TRUE; } HGFS_SET_TIME(inode->i_mtime, attr->writeTime); } else { needInvalidate = TRUE; LOG(4, (KERN_DEBUG "VMware hgfs: HgfsChangeFileAttributes: did not " "get mod time\n")); HGFS_SET_TIME(inode->i_mtime, HGFS_GET_CURRENT_TIME()); } /* * Windows doesn't know about ctime, and might send us something * bogus; if the ctime is invalid, use the mtime instead. */ if (attr->mask & HGFS_ATTR_VALID_CHANGE_TIME) { if (HGFS_SET_TIME(inode->i_ctime, attr->attrChangeTime)) { inode->i_ctime = inode->i_mtime; } } else { HGFS_SET_TIME(inode->i_ctime, HGFS_GET_CURRENT_TIME()); } /* * Compare old size and write time with new size and write time. If there's * a difference (or if we didn't get a new size or write time), the file * must have been written to, and we need to invalidate our cached pages. */ if (S_ISREG(inode->i_mode) && needInvalidate) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsChangeFileAttributes: file has " "changed on the server, invalidating pages.\n")); compat_filemap_write_and_wait(inode->i_mapping); compat_invalidate_remote_inode(inode); } } /* *---------------------------------------------------------------------- * * HgfsPrivateGetattr -- * * Internal getattr routine. Send a getattr request to the server * for the indicated remote name, and if it succeeds copy the * results of the getattr into the provided HgfsAttrInfo. * * attr->fileName will be allocated on success if the file is a * symlink; it's the caller's duty to free it. * * Results: * Returns zero on success, or a negative error on failure. * * Side effects: * None * *---------------------------------------------------------------------- */ int HgfsPrivateGetattr(struct dentry *dentry, // IN: Dentry containing name HgfsAttrInfo *attr) // OUT: Attr to copy into { struct HgfsSuperInfo *si; HgfsReq *req; HgfsStatus replyStatus; HgfsOp opUsed; int result = 0; HgfsRequest *requestHeader; Bool allowHandleReuse = TRUE; ASSERT(dentry); ASSERT(dentry->d_sb); ASSERT(attr); si = HGFS_SB_TO_COMMON(dentry->d_sb); req = HgfsGetNewRequest(); if (!req) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPrivateGetattr: out of memory " "while getting new request\n")); result = -ENOMEM; goto out; } requestHeader = (HgfsRequest *)(HGFS_REQ_PAYLOAD(req)); retry: opUsed = atomic_read(&hgfsVersionGetattr); result = HgfsPackGetattrRequest(req, dentry, allowHandleReuse, opUsed, attr); if (result != 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPrivateGetattr: no attrs\n")); goto out; } result = HgfsSendRequest(req); if (result == 0) { LOG(6, (KERN_DEBUG "VMware hgfs: HgfsPrivateGetattr: got reply\n")); replyStatus = HgfsReplyStatus(req); result = HgfsStatusConvertToLinux(replyStatus); /* * If the getattr succeeded on the server, copy the stats * into the HgfsAttrInfo, otherwise return an error. */ switch (result) { case 0: result = HgfsUnpackGetattrReply(req, attr); break; case -EBADF: /* * This can happen if we attempted a getattr by handle and the handle * was closed. Because we have no control over the backdoor, it's * possible that an attacker closed our handle, in which case the * driver still thinks the handle is open. So a straight-up * "goto retry" would cause an infinite loop. Instead, let's retry * with a getattr by name. */ if (allowHandleReuse) { allowHandleReuse = FALSE; goto retry; } /* * There's no reason why the server should have sent us this error * when we haven't used a handle. But to prevent an infinite loop in * the driver, let's make sure that we don't retry again. */ break; case -EPROTO: /* Retry with older version(s). Set globally. */ if (attr->requestType == HGFS_OP_GETATTR_V3) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPrivateGetattr: Version 3 " "not supported. Falling back to version 2.\n")); atomic_set(&hgfsVersionGetattr, HGFS_OP_GETATTR_V2); goto retry; } else if (attr->requestType == HGFS_OP_GETATTR_V2) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPrivateGetattr: Version 2 " "not supported. Falling back to version 1.\n")); atomic_set(&hgfsVersionGetattr, HGFS_OP_GETATTR); goto retry; } /* Fallthrough. */ default: break; } } else if (result == -EIO) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPrivateGetattr: timed out\n")); } else if (result == -EPROTO) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPrivateGetattr: server " "returned error: %d\n", result)); } else { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPrivateGetattr: unknown error: " "%d\n", result)); } out: HgfsFreeRequest(req); return result; } /* *----------------------------------------------------------------------------- * * HgfsIget -- * * Lookup or create an inode with the given attributes and remote filename. * * If an inode number of zero is specified, we'll extract an inode number * either from the attributes, or from calling iunique(). * * Results: * The inode on success * NULL on failure * * Side effects: * None * *----------------------------------------------------------------------------- */ struct inode * HgfsIget(struct super_block *sb, // IN: Superblock of this fs ino_t ino, // IN: Inode number (optional) HgfsAttrInfo const *attr) // IN: Attributes to create with { HgfsInodeInfo *iinfo; struct inode *inode; Bool isFakeInodeNumber = FALSE; ASSERT(sb); ASSERT(attr); LOG(6, (KERN_DEBUG "VMware hgfs: HgfsIget: entered\n")); /* No inode number? Use what's in the attributes, or call iunique(). */ if (ino == 0) { /* * Let's find out if the inode number the server gave us is already * in use. It's kind of lame that we have to do this, but that's what * we get when certain files have valid inode numbers and certain ones * don't. * * XXX: Is this worth the value? We're mixing server-provided inode * numbers with our own randomly chosen inode numbers. * * XXX: This logic is also racy. After our call to HgfsInodeLookup(), it's * possible another caller came in and grabbed that inode number, which * will cause us to collide in iget() and step on their inode. */ if (attr->mask & HGFS_ATTR_VALID_FILEID) { struct inode *oldInode; oldInode = HgfsInodeLookup(sb, attr->hostFileId); if (oldInode) { /* * If this inode's inode number was generated via iunique(), we * have a collision and cannot use the server's inode number. * Otherwise, we should reuse this inode. */ iinfo = INODE_GET_II_P(oldInode); if (iinfo->isFakeInodeNumber) { LOG(6, (KERN_DEBUG "VMware hgfs: HgfsIget: found existing " "iuniqued inode %"FMT64"d, generating new one\n", attr->hostFileId)); ino = iunique(sb, HGFS_RESERVED_INO); isFakeInodeNumber = TRUE; } else { LOG(6, (KERN_DEBUG "VMware hgfs: HgfsIget: found existing " "inode %"FMT64"d, reusing\n", attr->hostFileId)); ino = attr->hostFileId; } iput(oldInode); } else { ino = attr->hostFileId; } } else { /* * Get the next available inode number. There is a bit of a problem * with using iunique() in cases where HgfsIget was called to * instantiate an inode that's already in memory to a new dentry. In * such cases, we would like to get the old inode. But if we're * generating inode numbers with iunique(), we'll always have a new * inode number, thus we'll never get the old inode. This is * especially unfortunate when the old inode has some cached pages * attached to it that we won't be able to reuse. * * To mitigate this problem, whenever we use iunique() to generate an * inode number, we keep track of that fact in the inode. Then, when * we use ilookup() above to retrieve an inode, we only consider the * result a "collision" if the retrieved inode's inode number was set * via iunique(). Otherwise, we assume that we're reusing an inode * whose inode number was given to us by the server. */ ino = iunique(sb, HGFS_RESERVED_INO); isFakeInodeNumber = TRUE; } } LOG(6, (KERN_DEBUG "VMware hgfs: HgfsIget: calling iget on inode number " "%lu\n", ino)); /* Now we have a good inode number, get the inode itself. */ inode = HgfsGetInode(sb, ino); if (inode) { /* * On an allocation failure in read_super, the inode will have been * marked "bad". If it was, we certainly don't want to start playing with * the HgfsInodeInfo. So quietly put the inode back and fail. */ if (is_bad_inode(inode)) { LOG(6, (KERN_DEBUG "VMware hgfs: HgfsIget: encountered bad inode\n")); iput(inode); return NULL; } iinfo = INODE_GET_II_P(inode); iinfo->isFakeInodeNumber = isFakeInodeNumber; iinfo->isReferencedInode = TRUE; HgfsChangeFileAttributes(inode, attr); } LOG(6, (KERN_DEBUG "VMware hgfs: HgfsIget: done\n")); return inode; } /* *----------------------------------------------------------------------------- * * HgfsInstantiate -- * * Tie a dentry to a looked up or created inode. Callers may choose to * supply their own attributes, or may leave attr NULL in which case the * attributes will be queried from the server. Likewise, an inode number * of zero may be specified, in which case HgfsIget will get one from the * server or, barring that, from iunique(). * * Results: * Zero on success, negative error otherwise. * * Side effects: * None. * *----------------------------------------------------------------------------- */ int HgfsInstantiate(struct dentry *dentry, // IN: Dentry to use ino_t ino, // IN: Inode number (optional) HgfsAttrInfo const *attr) // IN: Attributes to use (optional) { struct inode *inode; HgfsAttrInfo newAttr; ASSERT(dentry); LOG(8, (KERN_DEBUG "VMware hgfs: HgfsInstantiate: entered\n")); /* If no specified attributes, get them from the server. */ if (attr == NULL) { int error; LOG(6, (KERN_DEBUG "VMware hgfs: HgfsInstantiate: issuing getattr\n")); newAttr.fileName = NULL; error = HgfsPrivateGetattr(dentry, &newAttr); if (error) { return error; } kfree(newAttr.fileName); attr = &newAttr; } /* * Get the inode with this inode number and the attrs we got from * the server. */ inode = HgfsIget(dentry->d_sb, ino, attr); if (!inode) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsInstantiate: out of memory " "getting inode\n")); return -ENOMEM; } /* Everything worked out, instantiate the dentry. */ LOG(8, (KERN_DEBUG "VMware hgfs: HgfsInstantiate: instantiating dentry\n")); HgfsDentryAgeReset(dentry); dentry->d_op = &HgfsDentryOperations; d_instantiate(dentry, inode); return 0; } /* *----------------------------------------------------------------------------- * * HgfsBuildPath -- * * Constructs the full path given a dentry by walking the dentry and its * parents back to the root. Adapted from d_path(), smb_build_path(), and * build_path_from_dentry() implementations in Linux 2.6.16. * * Results: * If non-negative, the length of the buffer written. * Otherwise, an error code. * * Side effects: * None * *----------------------------------------------------------------------------- */ int HgfsBuildPath(unsigned char *buffer, // IN/OUT: Buffer to write into size_t bufferLen, // IN: Size of buffer struct dentry *dentry) // IN: First dentry to walk { int retval = 0; size_t shortestNameLength; HgfsSuperInfo *si; char *originalBuffer; ASSERT(buffer); ASSERT(dentry); ASSERT(dentry->d_sb); si = HGFS_SB_TO_COMMON(dentry->d_sb); originalBuffer = buffer; /* * Buffer must hold at least the share name (which is already prefixed with * a forward slash), and nul. */ shortestNameLength = si->shareNameLen + 1; if (bufferLen < shortestNameLength) { return -ENAMETOOLONG; } memcpy(buffer, si->shareName, shortestNameLength); /* Short-circuit if we're at the root already. */ if (IS_ROOT(dentry)) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsBuildPath: Sending root \"%s\"\n", buffer)); return shortestNameLength; } /* Skip the share name, but overwrite our previous nul. */ buffer += shortestNameLength - 1; bufferLen -= shortestNameLength - 1; /* * Build the path string walking the tree backward from end to ROOT * and store it in reversed order. */ dget(dentry); compat_lock_dentry(dentry); while (!IS_ROOT(dentry)) { struct dentry *parent; size_t nameLen; nameLen = dentry->d_name.len; bufferLen -= nameLen + 1; if (bufferLen < 0) { compat_unlock_dentry(dentry); dput(dentry); LOG(4, (KERN_DEBUG "VMware hgfs: HgfsBuildPath: Ran out of space " "while writing dentry name\n")); return -ENAMETOOLONG; } buffer[bufferLen] = '/'; memcpy(buffer + bufferLen + 1, dentry->d_name.name, nameLen); retval += nameLen + 1; parent = dentry->d_parent; dget(parent); compat_unlock_dentry(dentry); dput(dentry); dentry = parent; compat_lock_dentry(dentry); } compat_unlock_dentry(dentry); dput(dentry); if (bufferLen == 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsBuildPath: Ran out of space while " "writing nul\n")); return -ENAMETOOLONG; } /* Shift the constructed string down to just past the share name. */ memmove(buffer, buffer + bufferLen, retval); buffer[retval] = '\0'; /* Don't forget the share name length (which also accounts for the nul). */ retval += shortestNameLength; LOG(4, (KERN_DEBUG "VMware hgfs: HgfsBuildPath: Built \"%s\"\n", originalBuffer)); return retval; } /* *----------------------------------------------------------------------------- * * HgfsDentryAgeReset -- * * Reset the age of this dentry by setting d_time to now. * * XXX: smb_renew_times from smbfs claims it is safe to reset the time of * all the parent dentries too, but how is that possible? If I stat a file * using a relative path, only that relative path will be validated. Sure, * it means that the parents still /exist/, but that doesn't mean their * attributes are up to date. * * Results: * None. * * Side effects: * None. * *----------------------------------------------------------------------------- */ void HgfsDentryAgeReset(struct dentry *dentry) // IN: Dentry whose age to reset { ASSERT(dentry); LOG(8, (KERN_DEBUG "VMware hgfs: HgfsDentryAgeReset: entered\n")); dget(dentry); compat_lock_dentry(dentry); dentry->d_time = jiffies; compat_unlock_dentry(dentry); dput(dentry); } /* *----------------------------------------------------------------------------- * * HgfsDentryAgeReset -- * * Set the dentry's time to 0. This makes the dentry's age "too old" and * forces subsequent HgfsRevalidates to go to the server for attributes. * * Results: * None. * * Side effects: * Subsequent HgfsRevalidate will not use cached attributes. * *----------------------------------------------------------------------------- */ void HgfsDentryAgeForce(struct dentry *dentry) // IN: Dentry we want to force { ASSERT(dentry); LOG(8, (KERN_DEBUG "VMware hgfs: HgfsDentryAgeForce: entered\n")); dget(dentry); compat_lock_dentry(dentry); dentry->d_time = 0; compat_unlock_dentry(dentry); dput(dentry); } /* *---------------------------------------------------------------------- * * HgfsGetOpenMode -- * * Based on the flags requested by the process making the open() * syscall, determine which open mode (access type) to request from * the server. * * Results: * Returns the correct HgfsOpenMode enumeration to send to the * server, or -1 on failure. * * Side effects: * None * *---------------------------------------------------------------------- */ int HgfsGetOpenMode(uint32 flags) // IN: Open flags { uint32 mask = O_RDONLY|O_WRONLY|O_RDWR; int result = -1; LOG(6, (KERN_DEBUG "VMware hgfs: HgfsGetOpenMode: entered\n")); /* * Mask the flags to only look at the access type. */ flags &= mask; /* Pick the correct HgfsOpenMode. */ switch (flags) { case O_RDONLY: result = HGFS_OPEN_MODE_READ_ONLY; break; case O_WRONLY: result = HGFS_OPEN_MODE_WRITE_ONLY; break; case O_RDWR: result = HGFS_OPEN_MODE_READ_WRITE; break; default: /* * This should never happen, but it could if a userlevel program * is behaving poorly. */ LOG(4, (KERN_DEBUG "VMware hgfs: HgfsGetOpenMode: invalid " "open flags %o\n", flags)); result = -1; break; } return result; } /* *----------------------------------------------------------------------------- * * HgfsCreateFileInfo -- * * Create the HGFS-specific file information struct and store a pointer to * it in the VFS file pointer. Also, link the file information struct in the * inode's file list, so that we may find it when all we have is an inode * (such as in writepage()). * * Results: * Zero if success, non-zero if error. * * Side effects: * None * *----------------------------------------------------------------------------- */ int HgfsCreateFileInfo(struct file *file, // IN: File pointer to attach to HgfsHandle handle) // IN: Handle returned from server { HgfsFileInfo *fileInfo; HgfsInodeInfo *inodeInfo; int mode; ASSERT(file); inodeInfo = INODE_GET_II_P(file->f_dentry->d_inode); ASSERT(inodeInfo); /* Get the mode of the opened file. */ mode = HgfsGetOpenMode(file->f_flags); if (mode < 0) { return -EINVAL; } /* * Store the file information for this open() in the file*. This needs * to be freed on a close(). Note that we trim all flags from the open * mode and increment it so that it is guaranteed to be non-zero, because * callers of HgfsGetHandle may pass in zero as the desired mode if they * don't care about the mode of the opened handle. * * XXX: Move this into a slab allocator once HgfsFileInfo is large. One day * soon, the kernel will allow us to embed the vfs file into our file info, * like we currently do for inodes. */ fileInfo = kmalloc(sizeof *fileInfo, GFP_KERNEL); if (!fileInfo) { return -ENOMEM; } fileInfo->handle = handle; fileInfo->mode = HGFS_OPEN_MODE_ACCMODE(mode) + 1; FILE_SET_FI_P(file, fileInfo); /* * I don't think we need any VFS locks since we're only touching the HGFS * specific state. But we should still acquire our own lock. * * XXX: Better granularity on locks, etc. */ spin_lock(&hgfsBigLock); list_add_tail(&fileInfo->list, &inodeInfo->files); spin_unlock(&hgfsBigLock); return 0; } /* *----------------------------------------------------------------------------- * * HgfsReleaseFileInfo -- * * Release HGFS-specific file information struct created in * HgfsCreateFileInfo. * * Results: * None * * Side effects: * None * *----------------------------------------------------------------------------- */ void HgfsReleaseFileInfo(struct file *file) // IN: File pointer to detach from { HgfsFileInfo *fileInfo; ASSERT(file); fileInfo = FILE_GET_FI_P(file); ASSERT(fileInfo); spin_lock(&hgfsBigLock); list_del_init(&fileInfo->list); spin_unlock(&hgfsBigLock); kfree(fileInfo); FILE_SET_FI_P(file, NULL); } /* *----------------------------------------------------------------------------- * * HgfsGetHandle -- * * Retrieve an existing HGFS handle for this inode, assuming one exists. * The handle retrieved satisfies the mode desired by the client. * * The desired mode does not correspond directly to HgfsOpenMode. Callers * should either increment the desired HgfsOpenMode, or, if any mode will * do, pass zero instead. This is in line with the Linux kernel's behavior * (see do_filp_open() and open_namei() for details). * * Results: * Zero on success, non-zero on error. * * Side effects: * None. * *----------------------------------------------------------------------------- */ int HgfsGetHandle(struct inode *inode, // IN: Inode to search for handles HgfsOpenMode mode, // IN: Mode to satisfy HgfsHandle *handle) // OUT: Retrieved HGFS handle { HgfsInodeInfo *iinfo; struct list_head *cur; Bool found = FALSE; ASSERT(handle); LOG(6, (KERN_DEBUG "VMware hgfs: HgfsGetHandle: desired mode %u\n", mode)); /* * We may have been called from a dentry without an associated inode. * HgfsReadSuper is one such caller. No inode means no open files, so * return an error. */ if (inode == NULL) { LOG(8, (KERN_DEBUG "VMware hgfs: HgfsGetHandle: NULL input\n")); return -EINVAL; } iinfo = INODE_GET_II_P(inode); /* * Unfortunately, we can't reuse handles belonging to directories. These * handles were created by a SearchOpen request, but the server itself * backed them with an artificial list of dentries populated via scandir. So * it can't actually use the handles for Getattr or Setattr requests, only * for subsequent SearchRead or SearchClose requests. */ if (S_ISDIR(inode->i_mode)) { LOG(8, (KERN_DEBUG "VMware hgfs: HgfsGetHandle: Called on directory\n")); return -EINVAL; } /* * Iterate over the open handles for this inode, and find one that allows * the given mode. A desired mode of zero means "any mode will do". * Otherwise return an error; */ spin_lock(&hgfsBigLock); list_for_each(cur, &iinfo->files) { HgfsFileInfo *finfo = list_entry(cur, HgfsFileInfo, list); if (mode == 0 || finfo->mode & mode) { *handle = finfo->handle; found = TRUE; break; } } spin_unlock(&hgfsBigLock); if (found) { LOG(6, (KERN_DEBUG "VMware hgfs: HgfsGetHandle: Returning handle %d\n", *handle)); return 0; } else { LOG(6, (KERN_DEBUG "VMware hgfs: HgfsGetHandle: Could not find matching " "handle\n")); return -ENOENT; } } /* *----------------------------------------------------------------------------- * * HgfsStatusConvertToLinux -- * * Convert a cross-platform HGFS status code to its Linux-kernel specific * counterpart. * * Rather than encapsulate the status codes within an array indexed by the * various HGFS status codes, we explicitly enumerate them in a switch * statement, saving the reader some time when matching HGFS status codes * against Linux status codes. * * Results: * Zero if the converted status code represents success, negative error * otherwise. Unknown status codes are converted to the more generic * "protocol error" status code to maintain forwards compatibility. * * Side effects: * None. * *----------------------------------------------------------------------------- */ int HgfsStatusConvertToLinux(HgfsStatus hgfsStatus) // IN: Status code to convert { switch (hgfsStatus) { case HGFS_STATUS_SUCCESS: return 0; case HGFS_STATUS_NO_SUCH_FILE_OR_DIR: case HGFS_STATUS_INVALID_NAME: return -ENOENT; case HGFS_STATUS_INVALID_HANDLE: return -EBADF; case HGFS_STATUS_OPERATION_NOT_PERMITTED: return -EPERM; case HGFS_STATUS_FILE_EXISTS: return -EEXIST; case HGFS_STATUS_NOT_DIRECTORY: return -ENOTDIR; case HGFS_STATUS_DIR_NOT_EMPTY: return -ENOTEMPTY; case HGFS_STATUS_PROTOCOL_ERROR: return -EPROTO; case HGFS_STATUS_ACCESS_DENIED: case HGFS_STATUS_SHARING_VIOLATION: return -EACCES; case HGFS_STATUS_NO_SPACE: return -ENOSPC; case HGFS_STATUS_OPERATION_NOT_SUPPORTED: return -EOPNOTSUPP; case HGFS_STATUS_NAME_TOO_LONG: return -ENAMETOOLONG; case HGFS_STATUS_GENERIC_ERROR: return -EIO; default: LOG(10, (KERN_DEBUG "VMware hgfs: HgfsStatusConvertToLinux: unknown " "error: %u\n", hgfsStatus)); return -EIO; } } /* *---------------------------------------------------------------------------- * * HgfsSetUidGid -- * * Sets the uid and gid of the host file represented by the provided * dentry. * * Note that this function assumes it is being called for a file that has * been created on the host with the correct gid if the sgid bit is set for * the parent directory. That is, we treat the presence of the sgid bit in * the parent direcory's mode as an indication not to set the gid manually * ourselves here. If we did, we would clobber the gid that the host file * system chose for us automatically when the file was created. * * Also note that the sgid bit itself would have been propagated to the new * file by the host file system as well. * * Results: * None. * * Side effects: * The host file's uid and gid are modified if the hgfs server has * permission to do so. * *---------------------------------------------------------------------------- */ void HgfsSetUidGid(struct inode *parent, // IN: parent inode struct dentry *dentry, // IN: dentry of file to update uid_t uid, // IN: uid to set gid_t gid) // IN: gid to set { struct iattr setUidGid; setUidGid.ia_valid = ATTR_UID; setUidGid.ia_uid = uid; /* * Only set the gid if the host file system wouldn't have for us. See the * comment in the function header. */ if (!parent || !(parent->i_mode & S_ISGID)) { setUidGid.ia_valid |= ATTR_GID; setUidGid.ia_gid = gid; } /* * After the setattr, we desperately want a revalidate so we can * get the true attributes from the server. However, the setattr * may have done that for us. To prevent a spurious revalidate, * reset the dentry's time before the setattr. That way, if setattr * ends up revalidating the dentry, the subsequent call to * revalidate will do nothing. */ HgfsDentryAgeForce(dentry); HgfsSetattr(dentry, &setUidGid); HgfsRevalidate(dentry); } /* *---------------------------------------------------------------------------- * * HgfsGetInode -- * * This function replaces iget() and should be called instead of it. In newer * kernels that have removed the iget() interface, GetInode() obtains an inode * and if it is a new one, then initializes the inode by calling * HgfsDoReadInode(). In older kernels that support the iget() interface, * HgfsDoReadInode() is called by iget() internally. * * Results: * A new inode object on success, NULL on error. * * Side effects: * None. * *---------------------------------------------------------------------------- */ struct inode * HgfsGetInode(struct super_block *sb, // IN: file system superblock object ino_t ino) // IN: inode number to assign to new inode { #ifdef VMW_USE_IGET_LOCKED struct inode *inode; inode = iget_locked(sb, ino); if (inode && (inode->i_state & I_NEW)) { HgfsDoReadInode(inode); unlock_new_inode(inode); } return inode; #else return iget(sb, ino); #endif } /* *---------------------------------------------------------------------------- * * HgfsDoReadInode -- * * A filesystem wide function that is called to initialize a new inode. * This is called from two different places depending on the kernel version. * In older kernels that provide the iget() interface, this function is * called by the kernel as part of inode initialization (from * HgfsDoReadInode). In newer kernels that call iget_locked(), this * function is called by filesystem code to initialize the new inode. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------------- */ void HgfsDoReadInode(struct inode *inode) // IN: Inode to initialize { HgfsInodeInfo *iinfo = INODE_GET_II_P(inode); /* * If the vfs inode is not embedded within the HgfsInodeInfo, then we * haven't yet allocated the HgfsInodeInfo. Do so now. * * XXX: We could allocate with GFP_ATOMIC. But instead, we'll do a standard * allocation and mark the inode "bad" if the allocation fails. This'll * make all subsequent operations on the inode fail, which is what we want. */ #ifndef VMW_EMBED_INODE iinfo = kmem_cache_alloc(hgfsInodeCache, GFP_KERNEL); if (!iinfo) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDoReadInode: no memory for " "iinfo!\n")); make_bad_inode(inode); return; } #endif INODE_SET_II_P(inode, iinfo); INIT_LIST_HEAD(&iinfo->files); iinfo->isReferencedInode = FALSE; iinfo->isFakeInodeNumber = FALSE; iinfo->createdAndUnopened = FALSE; } vmhgfs-only/fsutil.h0000444000000000000000000000761512025726746013526 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ /* * fsutil.h -- * * Functions used in more than one type of filesystem operation will be * exported from this file. */ #ifndef _HGFS_DRIVER_FSUTIL_H_ #define _HGFS_DRIVER_FSUTIL_H_ /* Must come before any kernel header file. */ #include "driver-config.h" #include #include "compat_fs.h" #include "inode.h" #include "request.h" #include "vm_basic_types.h" #include "hgfsProto.h" /* * Struct used to pass around attributes that Linux cares about. * These aren't just the attributes seen in HgfsAttr[V2]; we add a filename * pointer for convenience (used by SearchRead and Getattr). */ typedef struct HgfsAttrInfo { HgfsOp requestType; HgfsAttrValid mask; HgfsFileType type; /* File type */ uint64 size; /* File size (in bytes) */ uint64 accessTime; /* Time of last access */ uint64 writeTime; /* Time of last write */ uint64 attrChangeTime; /* Time file attributes were last changed */ HgfsPermissions specialPerms; /* Special permissions bits */ HgfsPermissions ownerPerms; /* Owner permissions bits */ HgfsPermissions groupPerms; /* Group permissions bits */ HgfsPermissions otherPerms; /* Other permissions bits */ uint32 userId; /* UID */ uint32 groupId; /* GID */ uint64 hostFileId; /* Inode number */ char *fileName; /* Either symlink target or filename */ } HgfsAttrInfo; /* Public functions (with respect to the entire module). */ int HgfsUnpackCommonAttr(HgfsReq *req, HgfsAttrInfo *attr); int HgfsEscapeBuffer(char const *bufIn, uint32 sizeIn, uint32 sizeBufOut, char *bufOut); int HgfsUnescapeBuffer(char *bufIn, uint32 sizeIn); void HgfsChangeFileAttributes(struct inode *inode, HgfsAttrInfo const *attr); int HgfsPrivateGetattr(struct dentry *dentry, HgfsAttrInfo *attr); struct inode *HgfsIget(struct super_block *sb, ino_t ino, HgfsAttrInfo const *attr); int HgfsInstantiate(struct dentry *dentry, ino_t ino, HgfsAttrInfo const *attr); int HgfsBuildPath(unsigned char *buffer, size_t bufferLen, struct dentry *dentry); void HgfsDentryAgeReset(struct dentry *dentry); void HgfsDentryAgeForce(struct dentry *dentry); int HgfsGetOpenMode(uint32 flags); int HgfsCreateFileInfo(struct file *file, HgfsHandle handle); void HgfsReleaseFileInfo(struct file *file); int HgfsGetHandle(struct inode *inode, HgfsOpenMode mode, HgfsHandle *handle); int HgfsStatusConvertToLinux(HgfsStatus hgfsStatus); void HgfsSetUidGid(struct inode *parent, struct dentry *dentry, uid_t uid, gid_t gid); struct inode *HgfsGetInode(struct super_block *sb, ino_t ino); void HgfsDoReadInode(struct inode *inode); #endif // _HGFS_DRIVER_FSUTIL_H_ vmhgfs-only/inode.c0000444000000000000000000017530612025726746013314 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ /* * inode.c -- * * Inode operations for the filesystem portion of the vmhgfs driver. */ /* Must come before any kernel header file. */ #include "driver-config.h" #include #include #include "compat_fs.h" #include "compat_highmem.h" #include "compat_kernel.h" #include "compat_mm.h" #include "compat_page-flags.h" #include "compat_spinlock.h" #include "compat_version.h" #include "cpName.h" #include "cpNameLite.h" #include "hgfsProto.h" #include "hgfsUtil.h" #include "inode.h" #include "module.h" #include "request.h" #include "fsutil.h" #include "vm_assert.h" /* * The inode_operations structure changed in 2.5.18: * before: * . 'getattr' was defined but unused * . 'revalidate' was defined and used * after: * 1) 'getattr' changed and became used * 2) 'revalidate' was removed * * Note: Mandrake backported 1) but not 2) starting with 2.4.8-26mdk * * --hpreg */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 18) # define HGFS_GETATTR_ONLY 1 #else # undef HGFS_GETATTR_ONLY #endif /* Private functions. */ static int HgfsDelete(struct inode *dir, struct dentry *dentry, HgfsOp op); static int HgfsPackSetattrRequest(struct iattr *iattr, struct dentry *dentry, Bool allowHandleReuse, HgfsOp opUsed, HgfsReq *req, Bool *changed); static int HgfsPackCreateDirRequest(struct dentry *dentry, int mode, HgfsOp opUsed, HgfsReq *req); static int HgfsTruncatePages(struct inode *inode, loff_t newSize); static int HgfsPackSymlinkCreateRequest(struct dentry *dentry, const char *symname, HgfsOp opUsed, HgfsReq *req); /* HGFS inode operations. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 75) static int HgfsCreate(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd); static struct dentry *HgfsLookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd); #else static int HgfsCreate(struct inode *dir, struct dentry *dentry, int mode); static struct dentry *HgfsLookup(struct inode *dir, struct dentry *dentry); #endif static int HgfsMkdir(struct inode *dir, struct dentry *dentry, int mode); static int HgfsRmdir(struct inode *dir, struct dentry *dentry); static int HgfsUnlink(struct inode *dir, struct dentry *dentry); static int HgfsRename(struct inode *oldDir, struct dentry *oldDentry, struct inode *newDir, struct dentry *newDentry); static int HgfsSymlink(struct inode *dir, struct dentry *dentry, const char *symname); #ifdef HGFS_GETATTR_ONLY static int HgfsGetattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat); #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) /* HGFS inode operations structure for directories. */ struct inode_operations HgfsDirInodeOperations = { /* Optional */ .create = HgfsCreate, /* Optional */ .mkdir = HgfsMkdir, .lookup = HgfsLookup, .rmdir = HgfsRmdir, .unlink = HgfsUnlink, .rename = HgfsRename, .symlink = HgfsSymlink, .setattr = HgfsSetattr, #ifdef HGFS_GETATTR_ONLY /* Optional */ .getattr = HgfsGetattr, #else /* Optional */ .revalidate = HgfsRevalidate, #endif }; /* HGFS inode operations structure for files. */ struct inode_operations HgfsFileInodeOperations = { .setattr = HgfsSetattr, #ifdef HGFS_GETATTR_ONLY /* Optional */ .getattr = HgfsGetattr, #else /* Optional */ .revalidate = HgfsRevalidate, #endif }; /* * Private functions implementations. */ /* *---------------------------------------------------------------------- * * HgfsDelete -- * * Handle both unlink and rmdir requests. * * Results: * Returns zero on success, or a negative error on failure. * * Side effects: * None * *---------------------------------------------------------------------- */ static int HgfsDelete(struct inode *dir, // IN: Parent dir of file/dir to delete struct dentry *dentry, // IN: Dentry of file/dir to delete HgfsOp op) // IN: Opcode for file type (file or dir) { HgfsReq *req = NULL; int result = 0; Bool secondAttempt = FALSE; HgfsStatus replyStatus; char *fileName = NULL; uint32 *fileNameLength; uint32 reqSize; HgfsOp opUsed; ASSERT(dir); ASSERT(dir->i_sb); ASSERT(dentry); ASSERT(dentry->d_inode); if (!dir || !dentry) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDelete: NULL input\n")); result = -EFAULT; goto out; } if ((op != HGFS_OP_DELETE_FILE) && (op != HGFS_OP_DELETE_DIR)) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDelete: Invalid opcode\n")); result = -EINVAL; goto out; } req = HgfsGetNewRequest(); if (!req) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDelete: out of memory while " "getting new request\n")); result = -ENOMEM; goto out; } retry: if (op == HGFS_OP_DELETE_FILE) { opUsed = atomic_read(&hgfsVersionDeleteFile); } else { opUsed = atomic_read(&hgfsVersionDeleteDir); } if (opUsed == HGFS_OP_DELETE_FILE_V3 || opUsed == HGFS_OP_DELETE_DIR_V3) { HgfsRequestDeleteV3 *request; HgfsRequest *header; header = (HgfsRequest *)(HGFS_REQ_PAYLOAD(req)); header->id = req->id; header->op = opUsed; request = (HgfsRequestDeleteV3 *)(HGFS_REQ_PAYLOAD_V3(req)); request->hints = 0; fileName = request->fileName.name; fileNameLength = &request->fileName.length; 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); } else { HgfsRequestDelete *request; request = (HgfsRequestDelete *)(HGFS_REQ_PAYLOAD(req)); /* Fill out the request packet. */ request->header.id = req->id; request->header.op = opUsed; fileName = request->fileName.name; fileNameLength = &request->fileName.length; reqSize = sizeof *request; } /* Build full name to send to server. */ if (HgfsBuildPath(fileName, HGFS_NAME_BUFFER_SIZET(reqSize), dentry) < 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDelete: build path failed\n")); result = -EINVAL; goto out; } LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDelete: deleting \"%s\", opUsed %u\n", fileName, opUsed)); /* Convert to CP name. */ result = CPName_ConvertTo(fileName, HGFS_NAME_BUFFER_SIZET(reqSize), fileName); if (result < 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDelete: CP conversion failed\n")); result = -EINVAL; goto out; } /* Unescape the CP name. */ result = HgfsUnescapeBuffer(fileName, result); *fileNameLength = result; req->payloadSize = reqSize + result; result = HgfsSendRequest(req); if (result == 0) { LOG(6, (KERN_DEBUG "VMware hgfs: HgfsDelete: got reply\n")); replyStatus = HgfsReplyStatus(req); result = HgfsStatusConvertToLinux(replyStatus); switch (result) { case 0: /* * Since we deleted the file, decrement its hard link count. As * we don't support hard links, this has the effect of making the * link count 0, which means that when the last reference to the * inode is dropped, the inode will be freed instead of moved to * the unused list. * * Also update the mtime/ctime of the parent directory, and the * ctime of the deleted file. */ compat_drop_nlink(dentry->d_inode); dentry->d_inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; break; case -EACCES: case -EPERM: /* * It's possible that we're talking to a Windows server with * a file marked read-only. Let's try again, after removing * the read-only bit from the file. * * XXX: I think old servers will send -EPERM here. Is this entirely * safe? */ if (!secondAttempt) { struct iattr enableWrite; secondAttempt = TRUE; LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDelete: access denied, " "attempting to work around read-only bit\n")); enableWrite.ia_mode = (dentry->d_inode->i_mode | S_IWUSR); enableWrite.ia_valid = ATTR_MODE; result = HgfsSetattr(dentry, &enableWrite); if (result == 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDelete: file is no " "longer read-only, retrying delete\n")); goto retry; } LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDelete: failed to remove " "read-only property\n")); } else { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDelete: second attempt at " "delete failed\n")); } break; case -EPROTO: /* Retry with older version(s). Set globally. */ if (opUsed == HGFS_OP_DELETE_DIR_V3) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDelete: Version 3 not " "supported. Falling back to version 1.\n")); atomic_set(&hgfsVersionDeleteDir, HGFS_OP_DELETE_DIR); goto retry; } else if (opUsed == HGFS_OP_DELETE_FILE_V3) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDelete: Version 3 not " "supported. Falling back to version 1.\n")); atomic_set(&hgfsVersionDeleteFile, HGFS_OP_DELETE_FILE); goto retry; } LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDelete: server " "returned error: %d\n", result)); break; default: break; } } else if (result == -EIO) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDelete: timed out\n")); } else if (result == -EPROTO) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDelete: server " "returned error: %d\n", result)); } else { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDelete: unknown error: " "%d\n", result)); } out: HgfsFreeRequest(req); return result; } /* *---------------------------------------------------------------------- * * HgfsPackSetattrRequest -- * * Setup the Setattr request, depending on the op version. When possible, * we will issue the setattr request using an existing open HGFS handle. * * Results: * Returns zero on success, or negative error on failure. * * On success, the changed argument is set indicating whether the * attributes have actually changed. * * Side effects: * None * *---------------------------------------------------------------------- */ static int HgfsPackSetattrRequest(struct iattr *iattr, // IN: Inode attrs to update from struct dentry *dentry, // IN: File to set attributes of Bool allowHandleReuse, // IN: Can we use a handle? HgfsOp opUsed, // IN: Op to be used HgfsReq *req, // IN/OUT: Packet to write into Bool *changed) // OUT: Have the attrs changed? { HgfsAttrV2 *attrV2; HgfsAttr *attr; HgfsAttrHint *hints; HgfsAttrChanges *update; HgfsHandle handle; char *fileName = NULL; uint32 *fileNameLength = NULL; unsigned int valid; size_t reqBufferSize; size_t reqSize; int result = 0; ASSERT(iattr); ASSERT(dentry); ASSERT(req); ASSERT(changed); valid = iattr->ia_valid; switch (opUsed) { case HGFS_OP_SETATTR_V3: { HgfsRequest *requestHeader; HgfsRequestSetattrV3 *requestV3; requestHeader = (HgfsRequest *)(HGFS_REQ_PAYLOAD(req)); requestHeader->op = opUsed; requestHeader->id = req->id; requestV3 = (HgfsRequestSetattrV3 *)HGFS_REQ_PAYLOAD_V3(req); attrV2 = &requestV3->attr; hints = &requestV3->hints; /* * Clear attributes, mask, and hints before touching them. * We can't rely on GetNewRequest() to zero our structures, so * make sure to zero them all here. */ memset(attrV2, 0, sizeof *attrV2); memset(hints, 0, sizeof *hints); /* * When possible, issue a setattr using an existing handle. This will * give us slightly better performance on a Windows server, and is more * correct regardless. If we don't find a handle, fall back on setattr * by name. * * Changing the size (via truncate) requires write permissions. Changing * the times also requires write permissions on Windows, so we require it * here too. Otherwise, any handle will do. */ if (allowHandleReuse && HgfsGetHandle(dentry->d_inode, (valid & ATTR_SIZE) || (valid & ATTR_ATIME) || (valid & ATTR_MTIME) ? HGFS_OPEN_MODE_WRITE_ONLY + 1 : 0, &handle) == 0) { requestV3->fileName.fid = handle; requestV3->fileName.flags = HGFS_FILE_NAME_USE_FILE_DESC; requestV3->fileName.caseType = HGFS_FILE_NAME_DEFAULT_CASE; requestV3->fileName.length = 0; LOG(6, (KERN_DEBUG "VMware hgfs: HgfsPackSetattrRequest: setting " "attributes of handle %u\n", handle)); } else { fileName = requestV3->fileName.name; fileNameLength = &requestV3->fileName.length; requestV3->fileName.caseType = HGFS_FILE_NAME_CASE_SENSITIVE; requestV3->fileName.fid = HGFS_INVALID_HANDLE; requestV3->fileName.flags = 0; } requestV3->reserved = 0; reqSize = HGFS_REQ_PAYLOAD_SIZE_V3(requestV3); reqBufferSize = HGFS_NAME_BUFFER_SIZET(reqSize); /* * We only support changing these attributes: * - all mode bits (i.e. all permissions) * - uid/gid * - size * - access/write times */ if (valid & ATTR_MODE) { attrV2->mask |= HGFS_ATTR_VALID_SPECIAL_PERMS | HGFS_ATTR_VALID_OWNER_PERMS | HGFS_ATTR_VALID_GROUP_PERMS | HGFS_ATTR_VALID_OTHER_PERMS; attrV2->specialPerms = ((iattr->ia_mode & (S_ISUID | S_ISGID | S_ISVTX)) >> 9); attrV2->ownerPerms = ((iattr->ia_mode & S_IRWXU) >> 6); attrV2->groupPerms = ((iattr->ia_mode & S_IRWXG) >> 3); attrV2->otherPerms = (iattr->ia_mode & S_IRWXO); *changed = TRUE; } if (valid & ATTR_UID) { attrV2->mask |= HGFS_ATTR_VALID_USERID; attrV2->userId = iattr->ia_uid; *changed = TRUE; } if (valid & ATTR_GID) { attrV2->mask |= HGFS_ATTR_VALID_GROUPID; attrV2->groupId = iattr->ia_gid; *changed = TRUE; } if (valid & ATTR_SIZE) { attrV2->mask |= HGFS_ATTR_VALID_SIZE; attrV2->size = iattr->ia_size; *changed = TRUE; } if (valid & ATTR_ATIME) { attrV2->mask |= HGFS_ATTR_VALID_ACCESS_TIME; attrV2->accessTime = HGFS_GET_TIME(iattr->ia_atime); if (valid & ATTR_ATIME_SET) { *hints |= HGFS_ATTR_HINT_SET_ACCESS_TIME; } *changed = TRUE; } if (valid & ATTR_MTIME) { attrV2->mask |= HGFS_ATTR_VALID_WRITE_TIME; attrV2->writeTime = HGFS_GET_TIME(iattr->ia_mtime); if (valid & ATTR_MTIME_SET) { *hints |= HGFS_ATTR_HINT_SET_WRITE_TIME; } *changed = TRUE; } break; } case HGFS_OP_SETATTR_V2: { HgfsRequestSetattrV2 *requestV2; requestV2 = (HgfsRequestSetattrV2 *)(HGFS_REQ_PAYLOAD(req)); requestV2->header.op = opUsed; requestV2->header.id = req->id; attrV2 = &requestV2->attr; hints = &requestV2->hints; /* * Clear attributes, mask, and hints before touching them. * We can't rely on GetNewRequest() to zero our structures, so * make sure to zero them all here. */ memset(attrV2, 0, sizeof *attrV2); memset(hints, 0, sizeof *hints); /* * When possible, issue a setattr using an existing handle. This will * give us slightly better performance on a Windows server, and is more * correct regardless. If we don't find a handle, fall back on setattr * by name. * * Changing the size (via truncate) requires write permissions. Changing * the times also requires write permissions on Windows, so we require it * here too. Otherwise, any handle will do. */ if (allowHandleReuse && HgfsGetHandle(dentry->d_inode, (valid & ATTR_SIZE) || (valid & ATTR_ATIME) || (valid & ATTR_MTIME) ? HGFS_OPEN_MODE_WRITE_ONLY + 1 : 0, &handle) == 0) { *hints = HGFS_ATTR_HINT_USE_FILE_DESC; requestV2->file = handle; LOG(6, (KERN_DEBUG "VMware hgfs: HgfsPackSetattrRequest: setting " "attributes of handle %u\n", handle)); } else { fileName = requestV2->fileName.name; fileNameLength = &requestV2->fileName.length; } reqSize = sizeof *requestV2; reqBufferSize = HGFS_NAME_BUFFER_SIZE(requestV2); /* * We only support changing these attributes: * - all mode bits (i.e. all permissions) * - uid/gid * - size * - access/write times */ if (valid & ATTR_MODE) { attrV2->mask |= HGFS_ATTR_VALID_SPECIAL_PERMS | HGFS_ATTR_VALID_OWNER_PERMS | HGFS_ATTR_VALID_GROUP_PERMS | HGFS_ATTR_VALID_OTHER_PERMS; attrV2->specialPerms = ((iattr->ia_mode & (S_ISUID | S_ISGID | S_ISVTX)) >> 9); attrV2->ownerPerms = ((iattr->ia_mode & S_IRWXU) >> 6); attrV2->groupPerms = ((iattr->ia_mode & S_IRWXG) >> 3); attrV2->otherPerms = (iattr->ia_mode & S_IRWXO); *changed = TRUE; } if (valid & ATTR_UID) { attrV2->mask |= HGFS_ATTR_VALID_USERID; attrV2->userId = iattr->ia_uid; *changed = TRUE; } if (valid & ATTR_GID) { attrV2->mask |= HGFS_ATTR_VALID_GROUPID; attrV2->groupId = iattr->ia_gid; *changed = TRUE; } if (valid & ATTR_SIZE) { attrV2->mask |= HGFS_ATTR_VALID_SIZE; attrV2->size = iattr->ia_size; *changed = TRUE; } if (valid & ATTR_ATIME) { attrV2->mask |= HGFS_ATTR_VALID_ACCESS_TIME; attrV2->accessTime = HGFS_GET_TIME(iattr->ia_atime); if (valid & ATTR_ATIME_SET) { *hints |= HGFS_ATTR_HINT_SET_ACCESS_TIME; } *changed = TRUE; } if (valid & ATTR_MTIME) { attrV2->mask |= HGFS_ATTR_VALID_WRITE_TIME; attrV2->writeTime = HGFS_GET_TIME(iattr->ia_mtime); if (valid & ATTR_MTIME_SET) { *hints |= HGFS_ATTR_HINT_SET_WRITE_TIME; } *changed = TRUE; } break; } case HGFS_OP_SETATTR: { HgfsRequestSetattr *request; request = (HgfsRequestSetattr *)(HGFS_REQ_PAYLOAD(req)); request->header.op = opUsed; request->header.id = req->id; attr = &request->attr; update = &request->update; /* We'll use these later. */ fileName = request->fileName.name; fileNameLength = &request->fileName.length; reqSize = sizeof *request; reqBufferSize = HGFS_NAME_BUFFER_SIZE(request); /* * Clear attributes before touching them. * We can't rely on GetNewRequest() to zero our structures, so * make sure to zero them all here. */ memset(attr, 0, sizeof *attr); memset(update, 0, sizeof *update); /* * We only support changing these attributes: * - owner mode bits (i.e. owner permissions) * - size * - access/write times */ if (valid & ATTR_MODE) { *update |= HGFS_ATTR_PERMISSIONS; attr->permissions = ((iattr->ia_mode & S_IRWXU) >> 6); *changed = TRUE; } if (valid & ATTR_SIZE) { *update |= HGFS_ATTR_SIZE; attr->size = iattr->ia_size; *changed = TRUE; } if (valid & ATTR_ATIME) { *update |= HGFS_ATTR_ACCESS_TIME | ((valid & ATTR_ATIME_SET) ? HGFS_ATTR_ACCESS_TIME_SET : 0); attr->accessTime = HGFS_GET_TIME(iattr->ia_atime); *changed = TRUE; } if (valid & ATTR_MTIME) { *update |= HGFS_ATTR_WRITE_TIME | ((valid & ATTR_MTIME_SET) ? HGFS_ATTR_WRITE_TIME_SET : 0); attr->writeTime = HGFS_GET_TIME(iattr->ia_mtime); *changed = TRUE; } break; } default: LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPackSetattrRequest: unexpected " "OP type encountered\n")); return -EPROTO; } /* Avoid all this extra work when we're doing a setattr by handle. */ if (fileName != NULL) { /* Build full name to send to server. */ if (HgfsBuildPath(fileName, reqBufferSize, dentry) < 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPackSetattrRequest: build path " "failed\n")); return -EINVAL; } LOG(6, (KERN_DEBUG "VMware hgfs: HgfsPackSetattrRequest: setting " "attributes of \"%s\"\n", fileName)); /* Convert to CP name. */ result = CPName_ConvertTo(fileName, reqBufferSize, fileName); if (result < 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPackSetattrRequest: CP " "conversion failed\n")); return -EINVAL; } /* Unescape the CP name. */ result = HgfsUnescapeBuffer(fileName, result); *fileNameLength = result; } req->payloadSize = reqSize + result; return 0; } /* *---------------------------------------------------------------------- * * HgfsPackCreateDirRequest -- * * Setup the CreateDir request, depending on the op version. * * Results: * Returns zero on success, or negative error on failure. * * Side effects: * None * *---------------------------------------------------------------------- */ static int HgfsPackCreateDirRequest(struct dentry *dentry, // IN: Directory to create int mode, // IN: Mode to assign dir HgfsOp opUsed, // IN: Op to be used. HgfsReq *req) // IN/OUT: Packet to write into { char *fileName = NULL; uint32 *fileNameLength; size_t requestSize; int result; ASSERT(dentry); ASSERT(req); switch (opUsed) { case HGFS_OP_CREATE_DIR_V3: { HgfsRequest *requestHeader; HgfsRequestCreateDirV3 *requestV3; requestHeader = (HgfsRequest *)(HGFS_REQ_PAYLOAD(req)); requestHeader->op = opUsed; requestHeader->id = req->id; requestV3 = (HgfsRequestCreateDirV3 *)(HGFS_REQ_PAYLOAD_V3(req)); /* We'll use these later. */ fileName = requestV3->fileName.name; fileNameLength = &requestV3->fileName.length; requestV3->fileName.flags = 0; requestV3->fileName.fid = HGFS_INVALID_HANDLE; requestV3->fileName.caseType = HGFS_FILE_NAME_CASE_SENSITIVE; requestSize = HGFS_REQ_PAYLOAD_SIZE_V3(requestV3); requestV3->mask = HGFS_CREATE_DIR_MASK; /* Set permissions. */ requestV3->specialPerms = (mode & (S_ISUID | S_ISGID | S_ISVTX)) >> 9; requestV3->ownerPerms = (mode & S_IRWXU) >> 6; requestV3->groupPerms = (mode & S_IRWXG) >> 3; requestV3->otherPerms = (mode & S_IRWXO); requestV3->reserved = 0; break; } case HGFS_OP_CREATE_DIR_V2: { HgfsRequestCreateDirV2 *requestV2; requestV2 = (HgfsRequestCreateDirV2 *)(HGFS_REQ_PAYLOAD(req)); requestV2->header.op = opUsed; requestV2->header.id = req->id; /* We'll use these later. */ fileName = requestV2->fileName.name; fileNameLength = &requestV2->fileName.length; requestSize = sizeof *requestV2; requestV2->mask = HGFS_CREATE_DIR_MASK; /* Set permissions. */ requestV2->specialPerms = (mode & (S_ISUID | S_ISGID | S_ISVTX)) >> 9; requestV2->ownerPerms = (mode & S_IRWXU) >> 6; requestV2->groupPerms = (mode & S_IRWXG) >> 3; requestV2->otherPerms = (mode & S_IRWXO); break; } case HGFS_OP_CREATE_DIR: { HgfsRequestCreateDir *request; request = (HgfsRequestCreateDir *)(HGFS_REQ_PAYLOAD(req)); /* We'll use these later. */ fileName = request->fileName.name; fileNameLength = &request->fileName.length; requestSize = sizeof *request; requestSize = sizeof *request; /* Set permissions. */ request->permissions = (mode & S_IRWXU) >> 6; break; } default: LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPackCreateDirRequest: unexpected " "OP type encountered\n")); return -EPROTO; } /* Build full name to send to server. */ if (HgfsBuildPath(fileName, HGFS_PACKET_MAX - (requestSize - 1), dentry) < 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPackCreateDirRequest: build path " "failed\n")); return -EINVAL; } LOG(6, (KERN_DEBUG "VMware hgfs: HgfsPackCreateDirRequest: create dir " "\"%s\", perms %o\n", fileName, mode)); /* Convert to CP name. */ result = CPName_ConvertTo(fileName, HGFS_PACKET_MAX - (requestSize - 1), fileName); if (result < 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPackCreateDirRequest: CP " "conversion failed\n")); return -EINVAL; } /* Unescape the CP name. */ result = HgfsUnescapeBuffer(fileName, result); *fileNameLength = result; req->payloadSize = requestSize + result; return 0; } /* *---------------------------------------------------------------------- * * HgfsTruncatePages -- * * Following a truncate operation on the server, we must update the * page cache's view of the file by truncating some pages. This is a * two step procedure. First we call vmtruncate() to truncate all * whole pages. Then we get the boundary page from the page cache * ourselves, compute where the truncation began, and memset() the * rest of the page to zero. * * Results: * Returns zero on success, or negative error on failure. * * Side effects: * None * *---------------------------------------------------------------------- */ static int HgfsTruncatePages(struct inode *inode, // IN: Inode whose page to truncate loff_t newSize) // IN: New size of the file { int result; pgoff_t pageIndex = newSize >> PAGE_CACHE_SHIFT; unsigned pageOffset = newSize & (PAGE_CACHE_SIZE - 1); struct page *page; char *buffer; ASSERT(inode); LOG(4, (KERN_DEBUG "VMware hgfs: HgfsTruncatePages: entered\n")); result = compat_vmtruncate(inode, newSize); if (result) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsTruncatePages: vmtruncate failed " "with error code %d\n", result)); return result; } /* * This is a bit complicated, so it merits an explanation. grab_cache_page() * will give us back the page with the specified index, after having locked * and incremented its reference count. We must first map it into memory so * we can modify it. After we're done modifying the page, we flush its data * from the data cache, unmap it, release our reference, and unlock it. */ page = grab_cache_page(inode->i_mapping, pageIndex); if (page == NULL) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsTruncatePages: could not get page " "with index %lu from page cache\n", pageIndex)); return -ENOMEM; } buffer = kmap(page); memset(buffer + pageOffset, 0, PAGE_CACHE_SIZE - pageOffset); flush_dcache_page(page); kunmap(page); page_cache_release(page); compat_unlock_page(page); return 0; } /* * HGFS inode operations. */ /* *---------------------------------------------------------------------- * * HgfsCreate -- * * Create inode for a new file. Called directly by vfs_create, * which is called by open_namei (both in fs/namei.c), as a result * of someone doing a creat(2) or an open(2) with O_CREAT. * * This gets called BEFORE f_op->open is called, so the file on the * remote end has not been created yet when we get here. So, we * just cheat and create a reasonable looking inode and instantiate * it. When this returns, our open routine will get called, which * will create the actual file on the server. If that fails for * some reason, dentry_open (which calls f_op->open) will cleanup * things and fput the dentry. * * XXX: Now that we do care about having valid inode numbers, it is * unfortunate but necessary that we "cheat" here. The problem is that * without the "intent" field from the nameidata struct (which we don't * get prior to 2.5.75), we have no way of knowing whether the file was * opened with O_EXCL or O_TRUNC. Knowing about O_TRUNC isn't crucial * because we can always create the file now and truncate it later, in * HgfsOpen. But without knowing about O_EXCL, we can't "fail if the file * exists on the server", which is the desired behavior for O_EXCL. The * source code for NFSv3 in 2.4.2 describes this shortcoming. The only * solution, barring massive architectural differences between the 2.4 and * 2.6 HGFS drivers, is to ignore O_EXCL, but we've supported it up until * now... * * Results: * Returns zero on success, negative error on failure. * * Side effects: * None * *---------------------------------------------------------------------- */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 75) static int HgfsCreate(struct inode *dir, // IN: Parent dir to create in struct dentry *dentry, // IN: Dentry containing name to create int mode, // IN: Mode of file to be created struct nameidata *nd) // IN: Intent, vfsmount, ... #else static int HgfsCreate(struct inode *dir, // IN: Parent dir to create in struct dentry *dentry, // IN: Dentry containing name to create int mode) // IN: Mode of file to be created #endif { HgfsAttrInfo attr; int result; ASSERT(dir); ASSERT(dentry); /* * We can call HgfsBuildPath and make the full path to this new entry, * but why bother if it's only for logging. */ LOG(6, (KERN_DEBUG "VMware hgfs: HgfsCreate: new entry \"%s\"\n", dentry->d_name.name)); /* Create appropriate attrs for this file. */ attr.type = HGFS_FILE_TYPE_REGULAR; attr.size = 0; /* just to be explicit */ attr.specialPerms = ((mode & (S_ISUID | S_ISGID | S_ISVTX)) >> 9); attr.ownerPerms = (mode & S_IRWXU) >> 6; attr.groupPerms = (mode & S_IRWXG) >> 3; attr.otherPerms = mode & S_IRWXO; attr.mask = HGFS_ATTR_VALID_TYPE | HGFS_ATTR_VALID_SIZE | HGFS_ATTR_VALID_SPECIAL_PERMS | HGFS_ATTR_VALID_OWNER_PERMS | HGFS_ATTR_VALID_GROUP_PERMS | HGFS_ATTR_VALID_OTHER_PERMS; result = HgfsInstantiate(dentry, 0, &attr); /* * Mark the inode as recently created but not yet opened so that if we do * fail to create the actual file in HgfsOpen, we know to force a * revalidate so that the next operation on this inode will fail. */ if (result == 0) { HgfsInodeInfo *iinfo = INODE_GET_II_P(dentry->d_inode); iinfo->createdAndUnopened = TRUE; } return result; } /* *---------------------------------------------------------------------- * * HgfsLookup -- * * Lookup a file in a directory. * * We do a getattr to see if the file exists on the server, and if * so we create a new inode and fill in the fields appropriately by * calling HgfsIget with the results of the getattr, and then * call d_add with the new dentry. * * For the curious, the way lookup in linux works (see fs/namei.c) * is roughly as follows: first a d_lookup is done to see if there * is an appropriate entry in the dcache already. If there is, it * is revalidated by calling d_op->d_revalidate, which calls our * HgfsDentryRevalidate (see above). If there is no dentry in the * cache or if the dentry is no longer valid, then namei calls * i_op->lookup, which calls HgfsLookup. * * Results: * Returns NULL on success, negative error on failure. * * Side effects: * None * *---------------------------------------------------------------------- */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 75) static struct dentry * HgfsLookup(struct inode *dir, // IN: Inode of parent directory struct dentry *dentry, // IN: Dentry containing name to look up struct nameidata *nd) // IN: Intent, vfsmount, ... #else static struct dentry * HgfsLookup(struct inode *dir, // IN: Inode of parent directory struct dentry *dentry) // IN: Dentry containing name to look up #endif { HgfsAttrInfo attr; struct inode *inode; int error = 0; ASSERT(dir); ASSERT(dentry); if (!dir || !dentry) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsLookup: NULL input\n")); error = -EFAULT; goto error; } LOG(6, (KERN_DEBUG "VMware hgfs: HgfsLookup: dir ino %lu, i_dev %u\n", dir->i_ino, dir->i_sb->s_dev)); LOG(6, (KERN_DEBUG "VMware hgfs: HgfsLookup: entry name is \"%s\"\n", dentry->d_name.name)); /* Do a getattr on the file to see if it exists on the server. */ inode = NULL; attr.fileName = NULL; error = HgfsPrivateGetattr(dentry, &attr); if (!error) { /* File exists on the server. */ /* * Get the inode with this inode number and the attrs we got from * the server. */ inode = HgfsIget(dir->i_sb, 0, &attr); kfree(attr.fileName); if (!inode) { error = -ENOMEM; LOG(4, (KERN_DEBUG "VMware hgfs: HgfsLookup: out of memory getting " "inode\n")); goto error; } } else if (error != -ENOENT) { /* * Either the file doesn't exist or there was a more serious * error; if it's the former, it's okay, we just do nothing. */ LOG(4, (KERN_DEBUG "VMware hgfs: HgfsLookup: error other " "than ENOENT: %d\n", error)); goto error; } /* * Set the dentry's time to NOW, set its operations pointer, add it * and the new (possibly NULL) inode to the dcache. */ HgfsDentryAgeReset(dentry); dentry->d_op = &HgfsDentryOperations; LOG(6, (KERN_DEBUG "VMware hgfs: HgfsLookup: adding new entry\n")); d_add(dentry, inode); return NULL; error: return ERR_PTR(error); } /* *---------------------------------------------------------------------- * * HgfsMkdir -- * * Handle a mkdir request * * Results: * Returns zero on success, or a negative error on failure. * * Side effects: * None * *---------------------------------------------------------------------- */ static int HgfsMkdir(struct inode *dir, // IN: Inode of parent directory struct dentry *dentry, // IN: Dentry with name to be created int mode) // IN: Mode of dir to be created { HgfsReq *req; HgfsStatus replyStatus; HgfsOp opUsed; int result = 0; ASSERT(dir); ASSERT(dir->i_sb); ASSERT(dentry); req = HgfsGetNewRequest(); if (!req) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsMkdir: out of memory while " "getting new request\n")); result = -ENOMEM; goto out; } retry: opUsed = atomic_read(&hgfsVersionCreateDir); result = HgfsPackCreateDirRequest(dentry, mode, opUsed, req); if (result != 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsMkdir: error packing request\n")); goto out; } /* * Send the request and process the reply. Since HgfsReplyCreateDirV2 and * HgfsReplyCreateDir are identical, we need no special logic here. */ result = HgfsSendRequest(req); if (result == 0) { LOG(6, (KERN_DEBUG "VMware hgfs: HgfsMkdir: got reply\n")); replyStatus = HgfsReplyStatus(req); result = HgfsStatusConvertToLinux(replyStatus); switch (result) { case 0: LOG(6, (KERN_DEBUG "VMware hgfs: HgfsMkdir: directory created " "successfully, instantiating dentry\n")); result = HgfsInstantiate(dentry, 0, NULL); if (result == 0) { /* * Attempt to set host directory's uid/gid to that of the * current user. As with the open(.., O_CREAT) case, this is * only expected to work when the hgfs server is running on * a Linux machine and as root, but we might as well give it * a go. */ HgfsSetUidGid(dir, dentry, current->fsuid, current->fsgid); } /* * XXX: When we support hard links, this is a good place to * increment link count of parent dir. */ break; case -EPROTO: /* Retry with older version(s). Set globally. */ if (opUsed == HGFS_OP_CREATE_DIR_V3) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsMkdir: Version 3 not " "supported. Falling back to version 2.\n")); atomic_set(&hgfsVersionCreateDir, HGFS_OP_CREATE_DIR_V2); goto retry; } else if (opUsed == HGFS_OP_CREATE_DIR_V2) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsMkdir: Version 2 not " "supported. Falling back to version 1.\n")); atomic_set(&hgfsVersionCreateDir, HGFS_OP_CREATE_DIR); goto retry; } /* Fallthrough. */ default: LOG(6, (KERN_DEBUG "VMware hgfs: HgfsMkdir: directory was not " "created, error %d\n", result)); break; } } else if (result == -EIO) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsMkdir: timed out\n")); } else if (result == -EPROTO) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsMkdir: server " "returned error: %d\n", result)); } else { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsMkdir: unknown error: " "%d\n", result)); } out: HgfsFreeRequest(req); return result; } /* *---------------------------------------------------------------------- * * HgfsRmdir -- * * Handle an rmdir request. Just calls HgfsDelete with the * correct opcode. * * Results: * Returns zero on success, or a negative error on failure. * * Side effects: * None * *---------------------------------------------------------------------- */ static int HgfsRmdir(struct inode *dir, // IN: Parent dir of dir to remove struct dentry *dentry) // IN: Dentry of dir to remove { int result; LOG(8, (KERN_DEBUG "VMware hgfs: HgfsRmdir: was called\n")); /* * XXX: CIFS also sets the size of the deleted directory to 0. Why? I don't * know...why not? * * XXX: When we support hardlinks, we should decrement the link count of * the parent directory. */ result = HgfsDelete(dir, dentry, HGFS_OP_DELETE_DIR); if (!result) { compat_i_size_write(dentry->d_inode, 0); } return result; } /* *---------------------------------------------------------------------- * * HgfsUnlink -- * * Handle an unlink request. Just calls HgfsDelete with the * correct opcode. * * Results: * Returns zero on success, or a negative error on failure. * * Side effects: * None * *---------------------------------------------------------------------- */ static int HgfsUnlink(struct inode *dir, // IN: Parent dir of file to unlink struct dentry *dentry) // IN: Dentry of file to unlink { LOG(8, (KERN_DEBUG "VMware hgfs: HgfsUnlink: was called\n")); return HgfsDelete(dir, dentry, HGFS_OP_DELETE_FILE); } /* *---------------------------------------------------------------------- * * HgfsRename -- * * Handle rename requests. * * Results: * Returns zero on success, or a negative error on failure. * * Side effects: * None * *---------------------------------------------------------------------- */ static int HgfsRename(struct inode *oldDir, // IN: Inode of original directory struct dentry *oldDentry, // IN: Dentry of file to rename struct inode *newDir, // IN: Inode of new directory struct dentry *newDentry) // IN: Dentry containing new name { HgfsReq *req = NULL; char *oldName; char *newName; uint32 *oldNameLength; uint32 *newNameLength; int result = 0; uint32 reqSize; HgfsOp opUsed; HgfsStatus replyStatus; ASSERT(oldDir); ASSERT(oldDir->i_sb); ASSERT(oldDentry); ASSERT(newDir); ASSERT(newDentry); if (!oldDir || !oldDentry || !newDir || !newDentry) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsRename: NULL input\n")); result = -EFAULT; goto out; } req = HgfsGetNewRequest(); if (!req) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsRename: out of memory while " "getting new request\n")); result = -ENOMEM; goto out; } retry: opUsed = atomic_read(&hgfsVersionRename); if (opUsed == HGFS_OP_RENAME_V3) { HgfsRequestRenameV3 *request = (HgfsRequestRenameV3 *)HGFS_REQ_PAYLOAD_V3(req); HgfsRequest *header = (HgfsRequest *)HGFS_REQ_PAYLOAD(req); header->op = opUsed; header->id = req->id; oldName = request->oldName.name; oldNameLength = &request->oldName.length; request->hints = 0; request->oldName.flags = 0; request->oldName.fid = HGFS_INVALID_HANDLE; request->oldName.caseType = HGFS_FILE_NAME_CASE_SENSITIVE; request->reserved = 0; reqSize = HGFS_REQ_PAYLOAD_SIZE_V3(request); } else { HgfsRequestRename *request = (HgfsRequestRename *)HGFS_REQ_PAYLOAD(req); request->header.op = opUsed; oldName = request->oldName.name; oldNameLength = &request->oldName.length; reqSize = sizeof *request; } /* Build full old name to send to server. */ if (HgfsBuildPath(oldName, HGFS_NAME_BUFFER_SIZET(reqSize), oldDentry) < 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsRename: build old path failed\n")); result = -EINVAL; goto out; } LOG(6, (KERN_DEBUG "VMware hgfs: HgfsRename: Old name: \"%s\"\n", oldName)); /* Convert old name to CP format. */ result = CPName_ConvertTo(oldName, HGFS_NAME_BUFFER_SIZET(reqSize), oldName); if (result < 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsRename: oldName CP " "conversion failed\n")); result = -EINVAL; goto out; } /* Unescape the old CP name. */ result = HgfsUnescapeBuffer(oldName, result); *oldNameLength = result; reqSize += result; /* * Build full new name to send to server. * Note the different buffer length. This is because HgfsRequestRename * contains two filenames, and once we place the first into the packet we * must account for it when determining the amount of buffer available for * the second. */ if (opUsed == HGFS_OP_RENAME_V3) { HgfsRequestRenameV3 *request = (HgfsRequestRenameV3 *)HGFS_REQ_PAYLOAD_V3(req); HgfsFileNameV3 *newNameP; newNameP = (HgfsFileNameV3 *)((char *)&request->oldName + sizeof request->oldName + result); newName = newNameP->name; newNameLength = &newNameP->length; newNameP->flags = 0; newNameP->fid = HGFS_INVALID_HANDLE; newNameP->caseType = HGFS_FILE_NAME_CASE_SENSITIVE; } else { HgfsRequestRename *request = (HgfsRequestRename *)HGFS_REQ_PAYLOAD(req); HgfsFileName *newNameP; newNameP = (HgfsFileName *)((char *)&request->oldName + sizeof request->oldName + result); newName = newNameP->name; newNameLength = &newNameP->length; } if (HgfsBuildPath(newName, HGFS_NAME_BUFFER_SIZET(reqSize) - result, newDentry) < 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsRename: build new path failed\n")); result = -EINVAL; goto out; } LOG(6, (KERN_DEBUG "VMware hgfs: HgfsRename: New name: \"%s\"\n", newName)); /* Convert new name to CP format. */ result = CPName_ConvertTo(newName, HGFS_NAME_BUFFER_SIZET(reqSize) - result, newName); if (result < 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsRename: newName CP " "conversion failed\n")); result = -EINVAL; goto out; } /* Unescape the new CP name. */ result = HgfsUnescapeBuffer(newName, result); *newNameLength = result; reqSize += result; req->payloadSize = reqSize; result = HgfsSendRequest(req); if (result == 0) { LOG(6, (KERN_DEBUG "VMware hgfs: HgfsRename: got reply\n")); replyStatus = HgfsReplyStatus(req); result = HgfsStatusConvertToLinux(replyStatus); if (result == -EPROTO) { /* Retry with older version(s). Set globally. */ if (opUsed == HGFS_OP_RENAME_V3) { atomic_set(&hgfsVersionRename, HGFS_OP_RENAME); goto retry; } else { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsRename: server " "returned error: %d\n", result)); goto out; } } } else if (result == -EIO) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsRename: timed out\n")); } else if (result == -EPROTO) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsRename: server " "returned error: %d\n", result)); } else { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsRename: unknown error: " "%d\n", result)); } out: HgfsFreeRequest(req); return result; } /* *---------------------------------------------------------------------- * * HgfsPackSymlinkCreateRequest -- * * Setup the create symlink request, depending on the op version. * * Results: * Returns zero on success, or negative error on failure. * * Side effects: * None * *---------------------------------------------------------------------- */ static int HgfsPackSymlinkCreateRequest(struct dentry *dentry, // IN: File pointer for this open const char *symname, // IN: Target name HgfsOp opUsed, // IN: Op to be used HgfsReq *req) // IN/OUT: Packet to write into { HgfsRequestSymlinkCreateV3 *requestV3 = NULL; HgfsRequestSymlinkCreate *request = NULL; char *symlinkName; uint32 *symlinkNameLength; char *targetName; uint32 *targetNameLength; size_t targetNameBytes; size_t requestSize; int result; ASSERT(dentry); ASSERT(symname); ASSERT(req); switch (opUsed) { case HGFS_OP_CREATE_SYMLINK_V3: { HgfsRequest *requestHeader; requestHeader = (HgfsRequest *)(HGFS_REQ_PAYLOAD(req)); requestHeader->op = opUsed; requestHeader->id = req->id; requestV3 = (HgfsRequestSymlinkCreateV3 *)HGFS_REQ_PAYLOAD_V3(req); /* We'll use these later. */ symlinkName = requestV3->symlinkName.name; symlinkNameLength = &requestV3->symlinkName.length; requestV3->symlinkName.flags = 0; requestV3->symlinkName.fid = HGFS_INVALID_HANDLE; requestV3->symlinkName.caseType = HGFS_FILE_NAME_CASE_SENSITIVE; requestV3->reserved = 0; requestSize = HGFS_REQ_PAYLOAD_SIZE_V3(requestV3); break; } case HGFS_OP_CREATE_SYMLINK: { request = (HgfsRequestSymlinkCreate *)(HGFS_REQ_PAYLOAD(req)); request->header.op = opUsed; request->header.id = req->id; /* We'll use these later. */ symlinkName = request->symlinkName.name; symlinkNameLength = &request->symlinkName.length; requestSize = sizeof *request; break; } default: LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPackSymlinkCreateRequest: unexpected " "OP type encountered\n")); return -EPROTO; } if (HgfsBuildPath(symlinkName, HGFS_PACKET_MAX - (requestSize - 1), dentry) < 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPackSymlinkCreateRequest: build symlink path " "failed\n")); return -EINVAL; } LOG(6, (KERN_DEBUG "VMware hgfs: HgfsPackSymlinkCreateRequest: Symlink name: \"%s\"\n", symlinkName)); /* Convert symlink name to CP format. */ result = CPName_ConvertTo(symlinkName, HGFS_PACKET_MAX - (requestSize - 1), symlinkName); if (result < 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPackSymlinkCreateRequest: symlinkName CP " "conversion failed\n")); return -EINVAL; } /* Unescape the symlink CP name. */ result = HgfsUnescapeBuffer(symlinkName, result); *symlinkNameLength = result; req->payloadSize = requestSize + result; /* * Note the different buffer length. This is because HgfsRequestSymlink * contains two filenames, and once we place the first into the packet we * must account for it when determining the amount of buffer available for * the second. * * Also note that targetNameBytes accounts for the NUL character. Once * we've converted it to CP name, it won't be NUL-terminated and the length * of the string in the packet itself won't account for it. */ if (opUsed == HGFS_OP_CREATE_SYMLINK_V3) { HgfsFileNameV3 *fileNameP; fileNameP = (HgfsFileNameV3 *)((char *)&requestV3->symlinkName + sizeof requestV3->symlinkName + result); targetName = fileNameP->name; targetNameLength = &fileNameP->length; fileNameP->flags = 0; fileNameP->fid = HGFS_INVALID_HANDLE; fileNameP->caseType = HGFS_FILE_NAME_CASE_SENSITIVE; } else { HgfsFileName *fileNameP; fileNameP = (HgfsFileName *)((char *)&request->symlinkName + sizeof request->symlinkName + result); targetName = fileNameP->name; targetNameLength = &fileNameP->length; } targetNameBytes = strlen(symname) + 1; /* Copy target name into request packet. */ if (targetNameBytes > HGFS_PACKET_MAX - (requestSize - 1)) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPackSymlinkCreateRequest: target name is too " "big\n")); return -EINVAL; } memcpy(targetName, symname, targetNameBytes); LOG(6, (KERN_DEBUG "VMware hgfs: HgfsPackSymlinkCreateRequest: target name: \"%s\"\n", targetName)); /* Convert target name to CPName-lite format. */ CPNameLite_ConvertTo(targetName, targetNameBytes - 1, '/'); /* Unescape the target CP-lite name. */ result = HgfsUnescapeBuffer(targetName, targetNameBytes - 1); *targetNameLength = result; req->payloadSize += result; return 0; } /* *---------------------------------------------------------------------- * * HgfsSymlink -- * * Handle a symlink request * * Results: * Returns zero on success, or a negative error on failure. * * Side effects: * None * *---------------------------------------------------------------------- */ static int HgfsSymlink(struct inode *dir, // IN: Inode of parent directory struct dentry *dentry, // IN: Dentry of new symlink file const char *symname) // IN: Target name { HgfsReq *req; int result = 0; HgfsOp opUsed; HgfsStatus replyStatus; ASSERT(dir); ASSERT(dir->i_sb); ASSERT(dentry); ASSERT(symname); req = HgfsGetNewRequest(); if (!req) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsSymlink: out of memory while " "getting new request\n")); result = -ENOMEM; goto out; } retry: opUsed = atomic_read(&hgfsVersionCreateSymlink); result = HgfsPackSymlinkCreateRequest(dentry, symname, opUsed, req); if (result != 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsSymlink: error packing request\n")); goto out; } result = HgfsSendRequest(req); if (result == 0) { LOG(6, (KERN_DEBUG "VMware hgfs: HgfsSymlink: got reply\n")); replyStatus = HgfsReplyStatus(req); result = HgfsStatusConvertToLinux(replyStatus); if (result == 0) { LOG(6, (KERN_DEBUG "VMware hgfs: HgfsSymlink: symlink created " "successfully, instantiating dentry\n")); result = HgfsInstantiate(dentry, 0, NULL); } else if (result == -EPROTO) { /* Retry with older version(s). Set globally. */ if (opUsed == HGFS_OP_CREATE_SYMLINK_V3) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsSymlink: Version 3 " "not supported. Falling back to version 2.\n")); atomic_set(&hgfsVersionCreateSymlink, HGFS_OP_CREATE_SYMLINK); goto retry; } else { LOG(6, (KERN_DEBUG "VMware hgfs: HgfsSymlink: symlink was not " "created, error %d\n", result)); } } } else if (result == -EIO) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsSymlink: timed out\n")); } else if (result == -EPROTO) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsSymlink: server " "returned error: %d\n", result)); } else { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsSymlink: unknown error: " "%d\n", result)); } out: HgfsFreeRequest(req); return result; } #ifdef HGFS_GETATTR_ONLY /* *----------------------------------------------------------------------------- * * HgfsGetattr -- * * Hgfs superblock 'getattr' method. * * Results: * 0 on success * error < 0 on failure * * Side effects: * None * *----------------------------------------------------------------------------- */ static int HgfsGetattr(struct vfsmount *mnt, // Unused struct dentry *dentry, // IN struct kstat *stat) // OUT { int err; // XXX ASSERT(mnt); ? --hpreg ASSERT(dentry); ASSERT(stat); err = HgfsRevalidate(dentry); if (err) { return err; } /* Convert stats from the VFS inode format to the kernel format --hpreg */ generic_fillattr(dentry->d_inode, stat); // XXX Should we set stat->blocks and stat->blksize? --hpreg return 0; } #endif /* * Public function implementations. */ /* *---------------------------------------------------------------------- * * HgfsSetattr -- * * Handle a setattr request. Call HgfsSetattrCopy to determine * which fields need updating and convert them to the HgfsAttr * format, then send the request to the server. * * Results: * Returns zero on success, or a negative error on failure. * * Side effects: * None * *---------------------------------------------------------------------- */ int HgfsSetattr(struct dentry *dentry, // IN: File to set attributes of struct iattr *iattr) // IN: Attributes to set { HgfsReq *req; HgfsStatus replyStatus; int result = 0; Bool changed = FALSE; Bool allowHandleReuse = TRUE; HgfsOp opUsed; ASSERT(dentry); ASSERT(dentry->d_inode); ASSERT(dentry->d_inode->i_mapping); ASSERT(dentry->d_sb); ASSERT(iattr); req = HgfsGetNewRequest(); if (!req) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsSetattr: out of memory while " "getting new request\n")); result = -ENOMEM; goto out; } retry: /* Fill out the request packet. */ opUsed = atomic_read(&hgfsVersionSetattr); result = HgfsPackSetattrRequest(iattr, dentry, allowHandleReuse, opUsed, req, &changed); if (result != 0 || !changed) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsSetattr: no attrs changed\n")); goto out; } /* * Flush all dirty pages prior to sending the request if we're going to * modify the file size. */ if (iattr->ia_valid & ATTR_SIZE) { compat_filemap_write_and_wait(dentry->d_inode->i_mapping); } /* Send the request and process the reply. */ result = HgfsSendRequest(req); if (result == 0) { /* Get the reply. */ replyStatus = HgfsReplyStatus(req); result = HgfsStatusConvertToLinux(replyStatus); switch (result) { case 0: /* * If we modified the file size, we must truncate our pages from the * page cache. */ if (iattr->ia_valid & ATTR_SIZE) { result = HgfsTruncatePages(dentry->d_inode, iattr->ia_size); } /* Fallthrough to revalidate. */ case -EPERM: /* * Now that the server's attributes are updated, let's update our * local view of them. Unfortunately, we can't trust iattr, because * the server may have chosen to ignore certain attributes that we * asked it to set. For example, a Windows server will have ignored * the mode nearly entirely. Therefore, rather than calling * inode_setattr() to update the inode with the contents of iattr, * just force a revalidate. * * XXX: Note that EPERM gets similar treatment, as the server may * have updated some of the attributes and still sent us an error. */ HgfsDentryAgeForce(dentry); HgfsRevalidate(dentry); break; case -EBADF: /* * This can happen if we attempted a setattr by handle and the handle * was closed. Because we have no control over the backdoor, it's * possible that an attacker closed our handle, in which case the * driver still thinks the handle is open. So a straight-up * "goto retry" would cause an infinite loop. Instead, let's retry * with a setattr by name. */ if (allowHandleReuse) { allowHandleReuse = FALSE; goto retry; } /* * There's no reason why the server should have sent us this error * when we haven't used a handle. But to prevent an infinite loop in * the driver, let's make sure that we don't retry again. */ break; case -EPROTO: /* Retry with older version(s). Set globally. */ if (opUsed == HGFS_OP_SETATTR_V3) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsSetattr: Version 3 " "not supported. Falling back to version 2.\n")); atomic_set(&hgfsVersionSetattr, HGFS_OP_SETATTR_V2); goto retry; } else if (opUsed == HGFS_OP_SETATTR_V2) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsSetattr: Version 2 " "not supported. Falling back to version 1.\n")); atomic_set(&hgfsVersionSetattr, HGFS_OP_SETATTR); goto retry; } /* Fallthrough. */ default: break; } } else if (result == -EIO) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsSetattr: timed out\n")); } else if (result == -EPROTO) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsSetattr: server " "returned error: %d\n", result)); } else { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsSetattr: unknown error: " "%d\n", result)); } out: HgfsFreeRequest(req); return result; } /* *---------------------------------------------------------------------- * * HgfsRevalidate -- * * Called when the kernel wants to check that an inode is still * valid. Called with the dentry that points to the inode we're * interested in. * * We call HgfsPrivateGetattr with the inode's remote name, and if * it succeeds we update the inode's attributes and return zero * (success). Otherwise, we return an error. * * Results: * Returns zero if inode is valid, negative error if not. * * Side effects: * None * *---------------------------------------------------------------------- */ int HgfsRevalidate(struct dentry *dentry) // IN: Dentry to revalidate { HgfsAttrInfo attr; int error = 0; HgfsSuperInfo *si; unsigned long age; ASSERT(dentry); si = HGFS_SB_TO_COMMON(dentry->d_sb); if (!dentry->d_inode) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsRevalidate: null input\n")); return -EINVAL; } LOG(6, (KERN_DEBUG "VMware hgfs: HgfsRevalidate: name %s, " "inum %lu\n", dentry->d_name.name, dentry->d_inode->i_ino)); age = jiffies - dentry->d_time; if (age > si->ttl) { LOG(6, (KERN_DEBUG "VMware hgfs: HgfsRevalidate: dentry is too old, " "getting new attributes\n")); /* * Sync unwritten file data so the file size on the server will * be current with our view of the file. */ compat_filemap_write_and_wait(dentry->d_inode->i_mapping); attr.fileName = NULL; error = HgfsPrivateGetattr(dentry, &attr); if (!error) { /* No error, so update inode's attributes and reset the age. */ HgfsChangeFileAttributes(dentry->d_inode, &attr); HgfsDentryAgeReset(dentry); kfree(attr.fileName); } } else { LOG(6, (KERN_DEBUG "VMware hgfs: HgfsRevalidate: using cached dentry " "attributes\n")); } return error; } vmhgfs-only/inode.h0000444000000000000000000000244612025726746013313 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ /* * inode.h -- * * Inode operations for the filesystem portion of the vmhgfs driver. */ #ifndef _HGFS_DRIVER_INODE_H_ #define _HGFS_DRIVER_INODE_H_ /* Must come before any kernel header file. */ #include "driver-config.h" #include "compat_fs.h" /* Public functions (with respect to the entire module). */ int HgfsSetattr(struct dentry *dentry, struct iattr *iattr); int HgfsRevalidate(struct dentry *dentry); #endif // _HGFS_DRIVER_INODE_H_ vmhgfs-only/link.c0000444000000000000000000001227012025726746013141 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ /* * link.c -- * * Symlink-specific inode operations for the filesystem portion of the * vmhgfs driver. */ /* Must come before any kernel header file. */ #include "driver-config.h" #include "compat_fs.h" #include "compat_namei.h" #include "module.h" #include "hgfsProto.h" #include "fsutil.h" #include "vm_assert.h" /* HGFS symlink operations. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13) static void *HgfsFollowlink(struct dentry *dentry, struct nameidata *nd); #else static int HgfsFollowlink(struct dentry *dentry, struct nameidata *nd); #endif static int HgfsReadlink(struct dentry *dentry, char __user *buffer, int buflen); /* HGFS inode operations structure for symlinks. */ struct inode_operations HgfsLinkInodeOperations = { .follow_link = HgfsFollowlink, .readlink = HgfsReadlink, }; /* * HGFS symlink operations. */ /* *---------------------------------------------------------------------- * * HgfsFollowlink -- * * Modeled after nfs_follow_link from a 2.4 kernel so it'll work * across all kernel revisions we care about. * * Results: * Returns zero on success, negative error on failure. * * On new kernels: The error is returned as void *. * On older kernels: The error is returned as is. * * Side effects: * None * *---------------------------------------------------------------------- */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13) static void * HgfsFollowlink(struct dentry *dentry, // IN: Dentry containing link struct nameidata *nd) // OUT: Contains target dentry #else static int HgfsFollowlink(struct dentry *dentry, // IN: Dentry containing link struct nameidata *nd) // OUT: Contains target dentry #endif { HgfsAttrInfo attr; int error; ASSERT(dentry); ASSERT(nd); if (!dentry) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsFollowlink: null input\n")); error = -EINVAL; goto out; } LOG(6, (KERN_DEBUG "VMware hgfs: HgfsFollowlink: calling " "HgfsPrivateGetattr\n")); attr.fileName = NULL; error = HgfsPrivateGetattr(dentry, &attr); if (!error) { /* Let's make sure we got called on a symlink. */ if (attr.type != HGFS_FILE_TYPE_SYMLINK || attr.fileName == NULL) { LOG(6, (KERN_DEBUG "VMware hgfs: HgfsFollowlink: got called " "on something that wasn't a symlink\n")); error = -EINVAL; } else { LOG(6, (KERN_DEBUG "VMware hgfs: HgfsFollowlink: calling " "vfs_follow_link\n")); error = vfs_follow_link(nd, attr.fileName); } kfree(attr.fileName); } out: #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13) return ERR_PTR(error); #else return error; #endif } /* *---------------------------------------------------------------------- * * HgfsReadlink -- * * Modeled after nfs_read_link from a 2.4 kernel so it'll work * across all kernel revisions we care about. * * Results: * Returns zero on success, negative error on failure. * * Side effects: * None * *---------------------------------------------------------------------- */ static int HgfsReadlink(struct dentry *dentry, // IN: Dentry containing link char __user *buffer, // OUT: User buffer to copy link into int buflen) // IN: Length of user buffer { HgfsAttrInfo attr; int error; ASSERT(dentry); ASSERT(buffer); if (!dentry) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsReadlink: null input\n")); return -EINVAL; } LOG(6, (KERN_DEBUG "VMware hgfs: HgfsReadlink: calling " "HgfsPrivateGetattr\n")); attr.fileName = NULL; error = HgfsPrivateGetattr(dentry, &attr); if (!error) { /* Let's make sure we got called on a symlink. */ if (attr.type != HGFS_FILE_TYPE_SYMLINK || attr.fileName == NULL) { LOG(6, (KERN_DEBUG "VMware hgfs: HgfsReadlink: got called " "on something that wasn't a symlink\n")); error = -EINVAL; } else { LOG(6, (KERN_DEBUG "VMware hgfs: HgfsReadlink: calling " "vfs_readlink\n")); error = vfs_readlink(dentry, buffer, buflen, attr.fileName); } kfree(attr.fileName); } return error; } vmhgfs-only/module.c0000444000000000000000000000571012025726746013472 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ /* * module.c -- * * Module-specific components of the vmhgfs driver. */ /* Must come before any kernel header file. */ #include "driver-config.h" #include #include "compat_module.h" #include "filesystem.h" #include "module.h" #include "vmhgfs_version.h" #ifdef VMX86_DEVEL /* * Logging is available only in devel build. */ int LOGLEVEL_THRESHOLD = 4; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 9) module_param(LOGLEVEL_THRESHOLD, int, 0444); #else MODULE_PARM(LOGLEVEL_THRESHOLD, "i"); #endif MODULE_PARM_DESC(LOGLEVEL_THRESHOLD, "Set verbosity (0 means no log, 10 means very verbose, 4 is default)"); #endif /* Module information. */ MODULE_AUTHOR("VMware, Inc."); MODULE_DESCRIPTION("VMware Host/Guest File System"); MODULE_VERSION(VMHGFS_DRIVER_VERSION_STRING); MODULE_LICENSE("GPL v2"); /* * Starting with SLE10sp2, Novell requires that IHVs sign a support agreement * with them and mark their kernel modules as externally supported via a * change to the module header. If this isn't done, the module will not load * by default (i.e., neither mkinitrd nor modprobe will accept it). */ MODULE_INFO(supported, "external"); /* *---------------------------------------------------------------------- * * init_module -- * * linux module entry point. Called by /sbin/insmod command. * Sets up internal state and registers the hgfs filesystem * with the kernel. * * Results: * Returns 0 on success, an error on failure. * * Side effects: * None * *---------------------------------------------------------------------- */ int init_module(void) { return HgfsInitFileSystem() ? 0 : -EBUSY; } /* *---------------------------------------------------------------------- * * cleanup_module -- * * Called by /sbin/rmmod. Unregisters filesystem with kernel, * cleans up internal state, and unloads module. * * Note: for true kernel 2.4 compliance, this should be * "module_exit". * * Results: * None * * Side effects: * None * *---------------------------------------------------------------------- */ void cleanup_module(void) { HgfsCleanupFileSystem(); } vmhgfs-only/module.h0000444000000000000000000002144212025726746013477 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ /* * module.h -- * * Global module definitions for the entire vmhgfs driver. */ #ifndef _HGFS_DRIVER_MODULE_H_ #define _HGFS_DRIVER_MODULE_H_ /* Must come before any kernel header file. */ #include "driver-config.h" #include #include "compat_completion.h" #include "compat_fs.h" #include "compat_kthread.h" #include "compat_semaphore.h" #include "compat_slab.h" #include "compat_spinlock.h" #include "compat_version.h" #include "rpcout.h" #include "hgfsProto.h" #ifndef __user #define __user #endif /* Logging stuff. */ #ifdef VMX86_DEVEL extern int LOGLEVEL_THRESHOLD; #define LOG(level, args) ((void) (LOGLEVEL_THRESHOLD >= (level) ? (printk args) : 0)) #else #define LOG(level, args) #endif /* Blocksize to be set in superblock. (XXX how is this used?) */ #define HGFS_BLOCKSIZE 1024 /* The amount of time we'll wait for the backdoor to process our request. */ #define HGFS_REQUEST_TIMEOUT (30 * HZ) /* * Inode number of the root inode. We set this to be non-zero because, * according to glibc source, when the returned inode number in a dirent * is zero, that entry has been deleted. This is presumably when you've done * an opendir, the file is deleted, and then you do a readdir. The point is * that if the root inode is zero, aliases to it (such as '.' and "..") won't * appear in a directory listing. */ #define HGFS_ROOT_INO 1 /* Leave HGFS_ROOT_INO and below out of inode number generation. */ #define HGFS_RESERVED_INO HGFS_ROOT_INO + 1 /* * Macros for accessing members that are private to this code in * sb/inode/file structs. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 42) #define HGFS_SET_SB_TO_COMMON(sb, common) do { (sb)->u.generic_sbp = (common); } while (0) #define HGFS_SB_TO_COMMON(sb) ((HgfsSuperInfo *)(sb)->u.generic_sbp) #else #define HGFS_SET_SB_TO_COMMON(sb, common) do { (sb)->s_fs_info = (common); } while (0) #define HGFS_SB_TO_COMMON(sb) ((HgfsSuperInfo *)(sb)->s_fs_info) #endif #ifdef VMW_EMBED_INODE #define INODE_GET_II_P(_inode) container_of(_inode, HgfsInodeInfo, inode) #elif defined(VMW_INODE_2618) #define INODE_GET_II_P(inode) ((HgfsInodeInfo *)(inode)->i_private) #else #define INODE_GET_II_P(inode) ((HgfsInodeInfo *)(inode)->u.generic_ip) #endif #if defined(VMW_INODE_2618) #define INODE_SET_II_P(inode, info) do { (inode)->i_private = (info); } while (0) #else #define INODE_SET_II_P(inode, info) do { (inode)->u.generic_ip = (info); } while (0) #endif /* 2.5.x kernels support nanoseconds timestamps. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 48) #define HGFS_DECLARE_TIME(unixtm) time_t unixtm #define HGFS_EQUAL_TIME(unixtm1, unixtm2) (unixtm1 == unixtm2) #define HGFS_SET_TIME(unixtm,nttime) HgfsConvertFromNtTime(&unixtm, nttime) #define HGFS_GET_TIME(unixtm) HgfsConvertToNtTime(unixtm, 0L) #define HGFS_GET_CURRENT_TIME() HgfsConvertToNtTime(CURRENT_TIME, 0L) /* * Beware! This macro returns list of two elements. Do not add braces around. */ #define HGFS_PRINT_TIME(unixtm) unixtm, 0L #else #define HGFS_DECLARE_TIME(unixtm) struct timespec unixtm #define HGFS_EQUAL_TIME(unixtm1, unixtm2) timespec_equal(&unixtm1, &unixtm2) #define HGFS_SET_TIME(unixtm,nttime) HgfsConvertFromNtTimeNsec(&unixtm, nttime) #define HGFS_GET_TIME(unixtm) HgfsConvertTimeSpecToNtTime(&unixtm) #define HGFS_GET_CURRENT_TIME() ({ \ struct timespec ct = CURRENT_TIME; \ HGFS_GET_TIME(ct); \ }) /* * Beware! This macro returns list of two elements. Do not add braces around. */ #define HGFS_PRINT_TIME(unixtm) unixtm.tv_sec, unixtm.tv_nsec #endif /* * The writeback support we're using (set_page_dirty()) was added in * 2.5.12, so we only support writeback from then on. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 12) #define HGFS_ENABLE_WRITEBACK #endif /* * For files opened in our actual Host/Guest filesystem, the * file->private_data field is used for storing the HgfsFileInfo of the * opened file. This macro is for accessing the file information from the * file *. */ #define FILE_SET_FI_P(file, info) do { (file)->private_data = info; } while (0) #define FILE_GET_FI_P(file) ((HgfsFileInfo *)(file)->private_data) /* * When waking up the request handler thread, these are the possible operations * one can ask it to perform. */ #define HGFS_REQ_THREAD_SEND (1 << 0) #define HGFS_REQ_THREAD_EXIT (1 << 1) /* Data kept in each superblock in sb->u. */ typedef struct HgfsSuperInfo { uid_t uid; /* UID of user who mounted this fs. */ Bool uidSet; /* Was the UID specified at mount-time? */ gid_t gid; /* GID of user who mounted this fs. */ Bool gidSet; /* Was the GID specified at mount-time? */ mode_t fmask; /* File permission mask. */ mode_t dmask; /* Directory permission mask. */ uint32 ttl; /* Maximum dentry age (in ticks). */ char *shareName; /* Mounted share name. */ size_t shareNameLen; /* To avoid repeated strlen() calls. */ } HgfsSuperInfo; /* * HGFS specific per-inode data. */ typedef struct HgfsInodeInfo { #ifdef VMW_EMBED_INODE /* Embedded inode. */ struct inode inode; #endif /* Was the inode number for this inode generated via iunique()? */ Bool isFakeInodeNumber; /* Is this a fake inode created in HgfsCreate that has yet to be opened? */ Bool createdAndUnopened; /* Is this inode referenced by HGFS? (needed by HgfsInodeLookup()) */ Bool isReferencedInode; /* List of open files for this inode. */ struct list_head files; } HgfsInodeInfo; /* * HGFS specific per-file data. */ typedef struct HgfsFileInfo { /* Links to place this object on the inode's list of open files. */ struct list_head list; /* Handle to be sent to the server. Needed for writepage(). */ HgfsHandle handle; /* * Mode with which handle was opened. When we reuse a handle, we need to * choose one with appropriate permissions. */ HgfsOpenMode mode; } HgfsFileInfo; /* * Global synchronization primitives. */ /* * We use hgfsBigLock to protect certain global structures that are locked for * a very short amount of time. */ extern spinlock_t hgfsBigLock; /* * The request handler thread uses hgfsReqThreadWait to wake up and handle * IO. Possible operations include: * -Sending outgoing HGFS requests. * -Shutting down the request handler thread. * * Finally, we use hgfsReqThread to synchronize the stopping of the * backdoor handler thread. */ extern long hgfsReqThreadFlags; extern wait_queue_head_t hgfsReqThreadWait; extern struct task_struct *hgfsReqThread; /* Hgfs filesystem structs. */ extern struct super_operations HgfsSuperOperations; extern struct dentry_operations HgfsDentryOperations; extern struct inode_operations HgfsFileInodeOperations; extern struct inode_operations HgfsDirInodeOperations; extern struct inode_operations HgfsLinkInodeOperations; extern struct file_operations HgfsFileFileOperations; extern struct file_operations HgfsDirFileOperations; extern struct address_space_operations HgfsAddressSpaceOperations; /* Other global state. */ extern compat_kmem_cache *hgfsReqCache; extern compat_kmem_cache *hgfsInodeCache; extern RpcOut *hgfsRpcOut; extern unsigned int hgfsIdCounter; extern struct list_head hgfsReqsUnsent; extern atomic_t hgfsVersionOpen; extern atomic_t hgfsVersionRead; extern atomic_t hgfsVersionWrite; extern atomic_t hgfsVersionClose; extern atomic_t hgfsVersionSearchOpen; extern atomic_t hgfsVersionSearchRead; extern atomic_t hgfsVersionSearchClose; extern atomic_t hgfsVersionGetattr; extern atomic_t hgfsVersionSetattr; extern atomic_t hgfsVersionCreateDir; extern atomic_t hgfsVersionDeleteFile; extern atomic_t hgfsVersionDeleteDir; extern atomic_t hgfsVersionRename; extern atomic_t hgfsVersionQueryVolumeInfo; extern atomic_t hgfsVersionCreateSymlink; #endif // _HGFS_DRIVER_MODULE_H_ vmhgfs-only/page.c0000444000000000000000000010133512025726746013121 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ /* * page.c -- * * Address space operations for the filesystem portion of the vmhgfs driver. */ /* Must come before any kernel header file. */ #include "driver-config.h" #include #include "compat_mm.h" #include "compat_page-flags.h" #include "compat_fs.h" #include "compat_kernel.h" #ifdef HGFS_ENABLE_WRITEBACK #include #endif #include "cpName.h" #include "hgfsProto.h" #include "module.h" #include "request.h" #include "hgfsUtil.h" #include "fsutil.h" #include "inode.h" #include "vm_assert.h" #include "vm_basic_types.h" /* Private functions. */ static int HgfsDoWrite(HgfsHandle handle, const char *buf, size_t count, loff_t offset); static int HgfsDoRead(HgfsHandle handle, char *buf, size_t count, loff_t offset); static int HgfsDoReadpage(HgfsHandle handle, struct page *page, unsigned pageFrom, unsigned pageTo); static int HgfsDoWritepage(HgfsHandle handle, struct page *page, unsigned pageFrom, unsigned pageTo); static void HgfsDoWriteBegin(struct page *page, unsigned pageFrom, unsigned pageTo); static int HgfsDoWriteEnd(struct file *file, struct page *page, unsigned pageFrom, unsigned pageTo, loff_t writeTo, unsigned copied); /* HGFS address space operations. */ static int HgfsReadpage(struct file *file, struct page *page); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 52) static int HgfsWritepage(struct page *page, struct writeback_control *wbc); #else static int HgfsWritepage(struct page *page); #endif /* * Write aop interface has changed in 2.6.28. Specifically, * the page locking semantics and requirement to handle * short writes. We already handle short writes, so no major * changes needed. write_begin is expected to return a locked * page and write_end is expected to unlock the page and drop * the reference before returning. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) static int HgfsWriteBegin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **page, void **clientData); static int HgfsWriteEnd(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned copied, struct page *page, void *clientData); #else static int HgfsPrepareWrite(struct file *file, struct page *page, unsigned pageFrom, unsigned pageTo); static int HgfsCommitWrite(struct file *file, struct page *page, unsigned pageFrom, unsigned pageTo); #endif /* HGFS address space operations structure. */ struct address_space_operations HgfsAddressSpaceOperations = { .readpage = HgfsReadpage, .writepage = HgfsWritepage, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) .write_begin = HgfsWriteBegin, .write_end = HgfsWriteEnd, #else .prepare_write = HgfsPrepareWrite, .commit_write = HgfsCommitWrite, #endif #ifdef HGFS_ENABLE_WRITEBACK .set_page_dirty = __set_page_dirty_nobuffers, #endif }; /* * Private functions. */ /* *----------------------------------------------------------------------------- * * HgfsDoRead -- * * Do one read request. Called by HgfsReadpage, possibly multiple times * if the size of the read is too big to be handled by one server request. * * We send a "Read" request to the server with the given handle. * * It is assumed that this function is never called with a larger read than * what can be sent in one request. * * Results: * Returns the number of bytes read on success, or an error on failure. * * Side effects: * None. * *---------------------------------------------------------------------------- */ static int HgfsDoRead(HgfsHandle handle, // IN: Handle for this file char *buf, // OUT: Buffer to copy data into size_t count, // IN: Number of bytes to read loff_t offset) // IN: Offset at which to read { HgfsReq *req; HgfsOp opUsed; int result = 0; uint32 actualSize = 0; char *payload = NULL; HgfsStatus replyStatus; ASSERT(buf); req = HgfsGetNewRequest(); if (!req) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDoRead: out of memory while " "getting new request\n")); result = -ENOMEM; goto out; } retry: opUsed = atomic_read(&hgfsVersionRead); if (opUsed == HGFS_OP_READ_V3) { HgfsRequest *header; HgfsRequestReadV3 *request; header = (HgfsRequest *)(HGFS_REQ_PAYLOAD(req)); header->id = req->id; header->op = opUsed; request = (HgfsRequestReadV3 *)(HGFS_REQ_PAYLOAD_V3(req)); request->file = handle; request->offset = offset; request->requiredSize = count; request->reserved = 0; req->payloadSize = HGFS_REQ_PAYLOAD_SIZE_V3(request); } else { HgfsRequestRead *request; request = (HgfsRequestRead *)(HGFS_REQ_PAYLOAD(req)); request->header.id = req->id; request->header.op = opUsed; request->file = handle; request->offset = offset; request->requiredSize = count; req->payloadSize = sizeof *request; } /* Send the request and process the reply. */ result = HgfsSendRequest(req); if (result == 0) { /* Get the reply. */ replyStatus = HgfsReplyStatus(req); result = HgfsStatusConvertToLinux(replyStatus); switch (result) { case 0: if (opUsed == HGFS_OP_READ_V3) { actualSize = ((HgfsReplyReadV3 *)HGFS_REP_PAYLOAD_V3(req))->actualSize; payload = ((HgfsReplyReadV3 *)HGFS_REP_PAYLOAD_V3(req))->payload; } else { actualSize = ((HgfsReplyRead *)HGFS_REQ_PAYLOAD(req))->actualSize; payload = ((HgfsReplyRead *)HGFS_REQ_PAYLOAD(req))->payload; } /* Sanity check on read size. */ if (actualSize > count) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDoRead: read too big!\n")); result = -EPROTO; goto out; } if (!actualSize) { /* We got no bytes, so don't need to copy to user. */ LOG(6, (KERN_DEBUG "VMware hgfs: HgfsDoRead: server returned " "zero\n")); result = actualSize; goto out; } /* Return result. */ memcpy(buf, payload, actualSize); LOG(6, (KERN_DEBUG "VMware hgfs: HgfsDoRead: copied %u\n", actualSize)); result = actualSize; break; case -EPROTO: /* Retry with older version(s). Set globally. */ if (opUsed == HGFS_OP_READ_V3) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDoRead: Version 3 not " "supported. Falling back to version 1.\n")); atomic_set(&hgfsVersionRead, HGFS_OP_READ); goto retry; } break; default: break; } } else if (result == -EIO) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDoRead: timed out\n")); } else if (result == -EPROTO) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDoRead: server " "returned error: %d\n", result)); } else { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDoRead: unknown error: " "%d\n", result)); } out: HgfsFreeRequest(req); return result; } /* *----------------------------------------------------------------------------- * * HgfsDoWrite -- * * Do one write request. Called by HgfsDoWritepage, possibly multiple * times if the size of the write is too big to be handled by one server * request. * * We send a "Write" request to the server with the given handle. * * It is assumed that this function is never called with a larger write * than what can be sent in one request. * * Results: * Returns the number of bytes written on success, or an error on failure. * * Side effects: * None. * *----------------------------------------------------------------------------- */ static int HgfsDoWrite(HgfsHandle handle, // IN: Handle for this file const char *buf, // IN: Buffer containing data size_t count, // IN: Number of bytes to write loff_t offset) // IN: Offset to begin writing at { HgfsReq *req; int result = 0; HgfsOp opUsed; uint32 requiredSize = 0; uint32 actualSize = 0; char *payload = NULL; uint32 reqSize; HgfsStatus replyStatus; ASSERT(buf); req = HgfsGetNewRequest(); if (!req) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDoWrite: out of memory while " "getting new request\n")); result = -ENOMEM; goto out; } retry: opUsed = atomic_read(&hgfsVersionWrite); if (opUsed == HGFS_OP_WRITE_V3) { HgfsRequest *header; HgfsRequestWriteV3 *request; header = (HgfsRequest *)(HGFS_REQ_PAYLOAD(req)); header->id = req->id; header->op = opUsed; request = (HgfsRequestWriteV3 *)(HGFS_REQ_PAYLOAD_V3(req)); request->file = handle; request->flags = 0; request->offset = offset; request->requiredSize = count; request->reserved = 0; payload = request->payload; requiredSize = request->requiredSize; reqSize = HGFS_REQ_PAYLOAD_SIZE_V3(request); } else { HgfsRequestWrite *request; request = (HgfsRequestWrite *)(HGFS_REQ_PAYLOAD(req)); request->header.id = req->id; request->header.op = opUsed; request->file = handle; request->flags = 0; request->offset = offset; request->requiredSize = count; payload = request->payload; requiredSize = request->requiredSize; reqSize = sizeof *request; } memcpy(payload, buf, requiredSize); req->payloadSize = reqSize + requiredSize - 1; /* Send the request and process the reply. */ result = HgfsSendRequest(req); if (result == 0) { /* Get the reply. */ replyStatus = HgfsReplyStatus(req); result = HgfsStatusConvertToLinux(replyStatus); switch (result) { case 0: if (opUsed == HGFS_OP_WRITE_V3) { actualSize = ((HgfsReplyWriteV3 *)HGFS_REP_PAYLOAD_V3(req))->actualSize; } else { actualSize = ((HgfsReplyWrite *)HGFS_REQ_PAYLOAD(req))->actualSize; } /* Return result. */ LOG(6, (KERN_DEBUG "VMware hgfs: HgfsDoWrite: wrote %u bytes\n", actualSize)); result = actualSize; break; case -EPROTO: /* Retry with older version(s). Set globally. */ if (opUsed == HGFS_OP_WRITE_V3) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDoWrite: Version 3 not " "supported. Falling back to version 1.\n")); atomic_set(&hgfsVersionWrite, HGFS_OP_WRITE); goto retry; } break; default: LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDoWrite: server " "returned error: %d\n", result)); break; } } else if (result == -EIO) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDoWrite: timed out\n")); } else if (result == -EPROTO) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDoWrite: server " "returned error: %d\n", result)); } else { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDoWrite: unknown error: " "%d\n", result)); } out: HgfsFreeRequest(req); return result; } /* *----------------------------------------------------------------------------- * * HgfsDoReadpage -- * * Reads in a single page, using the specified handle and page offsets. * At the time of writing, HGFS_IO_MAX == PAGE_CACHE_SIZE, so we could * avoid the do {} while() and just read the page as is, but in case the * above assumption is ever broken, it's nice that this will continue to * "just work". * * Results: * Zero on success, non-zero on error. * * Side effects: * None. * *----------------------------------------------------------------------------- */ static int HgfsDoReadpage(HgfsHandle handle, // IN: Handle to use for reading struct page *page, // IN/OUT: Page to read into unsigned pageFrom, // IN: Where to start reading to unsigned pageTo) // IN: Where to stop reading { int result = 0; char *buffer = kmap(page) + pageFrom; loff_t curOffset = ((loff_t)page->index << PAGE_CACHE_SHIFT) + pageFrom; size_t nextCount, remainingCount = pageTo - pageFrom; LOG(6, (KERN_DEBUG "VMware hgfs: HgfsDoReadpage: read %Zu bytes from fh %u " "at offset %Lu\n", remainingCount, handle, curOffset)); /* * Call HgfsDoRead repeatedly until either * - HgfsDoRead returns an error, or * - HgfsDoRead returns 0 (end of file), or * - We have read the requested number of bytes. */ do { nextCount = (remainingCount > HGFS_IO_MAX) ? HGFS_IO_MAX : remainingCount; result = HgfsDoRead(handle, buffer, nextCount, curOffset); if (result < 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDoReadpage: read error %d\n", result)); goto out; } remainingCount -= result; curOffset += result; buffer += result; } while ((result > 0) && (remainingCount > 0)); /* * It's possible that despite being asked to read a full page, there is less * than a page in the file from this offset, so we should zero the rest of * the page's memory. */ memset(buffer, 0, remainingCount); /* * We read a full page (or all of the page that actually belongs to the * file), so mark it up to date. Also, flush the old page data from the data * cache. */ flush_dcache_page(page); SetPageUptodate(page); result = 0; out: kunmap(page); return result; } /* *----------------------------------------------------------------------------- * * HgfsDoWritepage -- * * Writes out a single page, using the specified handle and page offsets. * At the time of writing, HGFS_IO_MAX == PAGE_CACHE_SIZE, so we could * avoid the do {} while() and just write the page as is, but in case the * above assumption is ever broken, it's nice that this will continue to * "just work". * * A quick note about appending to files. Before HGFS used the page cache, * an HgfsWrite examined a file's f_flags and added HGFS_WRITE_APPEND to * the write packet if the file was opened with O_APPEND. This causes the * server to reopen the fd with O_APPEND so that writes will append to the * end. * * In the page cache world, this won't work because we may have arrived at * this function via writepage(), which doesn't give us a particular file * and thus we don't know if we should be appending or not. In fact, the * generic write path employed by the page cache handles files with O_APPEND * set by moving the file offset to the result of i_size_read(). So we * shouldn't ever need to set HGFS_WRITE_APPEND, as now we will handle all * write appends, instead of telling the server to do it for us. * * Results: * Zero on success, non-zero on error. * * Side effects: * None. * *----------------------------------------------------------------------------- */ static int HgfsDoWritepage(HgfsHandle handle, // IN: Handle to use for writing struct page *page, // IN: Page containing data to write unsigned pageFrom, // IN: Beginning page offset unsigned pageTo) // IN: Ending page offset { int result = 0; char *buffer = kmap(page) + pageFrom; loff_t curOffset = ((loff_t)page->index << PAGE_CACHE_SHIFT) + pageFrom; size_t nextCount; size_t remainingCount = pageTo - pageFrom; struct inode *inode; ASSERT(page->mapping); ASSERT(page->mapping->host); inode = page->mapping->host; /* * Call HgfsDoWrite repeatedly until either * - HgfsDoWrite returns an error, or * - HgfsDoWrite returns 0 (XXX this probably rarely happens), or * - We have written the requested number of bytes. */ do { nextCount = (remainingCount > HGFS_IO_MAX) ? HGFS_IO_MAX : remainingCount; result = HgfsDoWrite(handle, buffer, nextCount, curOffset); if (result < 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsDoWritepage: write error %d\n", result)); goto out; } remainingCount -= result; curOffset += result; buffer += result; /* Update the inode's size now rather than waiting for a revalidate. */ if (curOffset > compat_i_size_read(inode)) { compat_i_size_write(inode, curOffset); } } while ((result > 0) && (remainingCount > 0)); result = 0; out: kunmap(page); return result; } /* * HGFS address space operations. */ /* *----------------------------------------------------------------------------- * * HgfsReadpage -- * * Read a page from an open file. Like HgfsWritepage, there are some * complicated locking rules governing this function. The page arrives from * the VFS locked, and we must unlock it before exiting. In addition, we * must acquire a reference to the page before mapping it, and we must * flush the page's data from the data cache (not to be confused with * dcache i.e. the dentry cache). * * Results: * Zero on success, non-zero on error. * * Side effects: * None. * *----------------------------------------------------------------------------- */ static int HgfsReadpage(struct file *file, // IN: File to read from struct page *page) // IN/OUT: Page to write to { int result = 0; HgfsHandle handle; ASSERT(file); ASSERT(file->f_dentry); ASSERT(file->f_dentry->d_inode); ASSERT(page); handle = FILE_GET_FI_P(file)->handle; LOG(6, (KERN_DEBUG "VMware hgfs: HgfsReadPage: reading from handle %u\n", handle)); page_cache_get(page); result = HgfsDoReadpage(handle, page, 0, PAGE_CACHE_SIZE); page_cache_release(page); compat_unlock_page(page); return result; } /* *----------------------------------------------------------------------------- * * HgfsWritepage -- * * The "spontaneous" way to write a page, called when the kernel is under * memory pressure or is asked to sync a memory mapped file. Because * writepage() can be called from so many different places, we don't get a * filp with which to write, and we have to be very careful about races and * locking. * * Results: * Zero on success, non-zero on error. * * Side effects: * None. * *----------------------------------------------------------------------------- */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 52) static int HgfsWritepage(struct page *page, // IN: Page to write from struct writeback_control *wbc) // IN: Ignored #else static int HgfsWritepage(struct page *page) // IN: Page to write from #endif { struct inode *inode; HgfsHandle handle; int result; pgoff_t lastPageIndex; loff_t currentFileSize; unsigned to = PAGE_CACHE_SIZE; ASSERT(page); ASSERT(page->mapping); ASSERT(page->mapping->host); inode = page->mapping->host; /* We need a writable file handle. */ result = HgfsGetHandle(inode, HGFS_OPEN_MODE_WRITE_ONLY + 1, &handle); if (result) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsWritepage: could not get writable " "file handle\n")); goto exit; } /* * We were given an entire page to write. In most cases this means "start * writing from the beginning of the page (byte 0) to the very end (byte * PAGE_CACHE_SIZE). But what if this is the last page of the file? Then * we don't want to write a full PAGE_CACHE_SIZE bytes, but just however * many bytes may remain in the page. * * XXX: Other filesystems check the page index to make sure that the page * we're being asked to write is within the size of the file. I guess * that's because writepage() can race with truncate(), and if we find * ourselves here after a truncate(), we can drop the write. */ currentFileSize = compat_i_size_read(inode); lastPageIndex = currentFileSize >> PAGE_CACHE_SHIFT; if (page->index > lastPageIndex) { goto exit; } else if (page->index == lastPageIndex) { to = currentFileSize & (PAGE_CACHE_SIZE - 1); if (to == 0) { goto exit; } } /* * This part is fairly intricate, so it deserves some explanation. We're * really interested in calling HgfsDoWritepage with our page and * handle, without having to then worry about locks or references. See * Documentation/filesystems/Locking in the kernel to see what rules we * must obey. * * Firstly, we acquire a reference to the page via page_cache_get() and call * compat_set_page_writeback(). The latter does a number of things: it sets * the writeback bit on the page, and if it wasn't already set, it sets the * writeback bit in the radix tree. Then, if the page isn't dirty, it clears * the dirty bit in the radix tree. The end result is that the radix tree's * notion of dirty and writeback is fully synced with the page itself. * * Secondly, we write the page itself. * * Thirdly, we end writeback of the page via compat_end_page_writeback(), * and release our reference on the page. * * Finally, we unlock the page, waking up its waiters and making it * available to anyone else. Note that this step must be performed * regardless of whether we wrote anything, as the VFS locked the page for * us. */ page_cache_get(page); compat_set_page_writeback(page); result = HgfsDoWritepage(handle, page, 0, to); compat_end_page_writeback(page); page_cache_release(page); exit: compat_unlock_page(page); return result; } /* *----------------------------------------------------------------------------- * * HgfsDoWriteBegin -- * * Helper function for HgfsWriteBegin / HgfsPrepareWrite. * * Initialize the page if the file is to be appended. * * Results: * None. * * Side effects: * None. * *----------------------------------------------------------------------------- */ static void HgfsDoWriteBegin(struct page *page, // IN: Page to be written unsigned pageFrom, // IN: Starting page offset unsigned pageTo) // IN: Ending page offset { ASSERT(page); #ifdef HGFS_ENABLE_WRITEBACK loff_t offset = (loff_t)page->index << PAGE_CACHE_SHIFT; loff_t currentFileSize = compat_i_size_read(page->mapping->host); /* * If we are doing a partial write into a new page (beyond end of * file), then intialize it. This allows other writes to this page * to accumulate before we need to write it to the server. */ if ((offset >= currentFileSize) || ((pageFrom == 0) && (offset + pageTo) >= currentFileSize)) { void *kaddr = kmap_atomic(page, KM_USER0); if (pageFrom) { memset(kaddr, 0, pageFrom); } if (pageTo < PAGE_CACHE_SIZE) { memset(kaddr + pageTo, 0, PAGE_CACHE_SIZE - pageTo); } kunmap_atomic(kaddr, KM_USER0); flush_dcache_page(page); } #endif } #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28) /* *----------------------------------------------------------------------------- * * HgfsPrepareWrite -- * * Called by the generic write path to set up a write request for a page. * We're expected to do any pre-allocation and housekeeping prior to * receiving the write. * * Results: * Always zero. * * Side effects: * None. * *----------------------------------------------------------------------------- */ static int HgfsPrepareWrite(struct file *file, // IN: Ignored struct page *page, // IN: Page to prepare unsigned pageFrom, // IN: Beginning page offset unsigned pageTo) // IN: Ending page offset { HgfsDoWriteBegin(page, pageFrom, pageTo); /* * Prior to 2.4.10, our caller expected to call page_address(page) between * the calls to prepare_write() and commit_write(). This meant filesystems * had to kmap() the page in prepare_write() and kunmap() it in * commit_write(). In 2.4.10, the call to page_address() was replaced with * __copy_to_user(), and while its not clear to me why this is safer, * nfs_prepare_write() dropped the kmap()/kunmap() calls in the same patch, * so the two events must be related. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 10) kmap(page); #endif return 0; } #else /* *----------------------------------------------------------------------------- * * HgfsWriteBegin -- * * Called by the generic write path to set up a write request for a page. * We're expected to do any pre-allocation and housekeeping prior to * receiving the write. * * This function is expected to return a locked page. * * Results: * Zero on success, non-zero error otherwise. * * Side effects: * None. * *----------------------------------------------------------------------------- */ static int HgfsWriteBegin(struct file *file, // IN: File to be written struct address_space *mapping, // IN: Mapping loff_t pos, // IN: File position unsigned len, // IN: Bytes to be written unsigned flags, // IN: Write flags struct page **pagePtr, // OUT: Locked page void **clientData) // OUT: Opaque to pass to write_end, unused { pgoff_t index = pos >> PAGE_CACHE_SHIFT; unsigned pageFrom = pos & (PAGE_CACHE_SHIFT - 1); unsigned pageTo = pos + len; struct page *page; page = __grab_cache_page(mapping, index); if (page == NULL) { return -ENOMEM; } *pagePtr = page; HgfsDoWriteBegin(page, pageFrom, pageTo); return 0; } #endif /* *----------------------------------------------------------------------------- * * HgfsDoWriteEnd -- * * Helper function for HgfsWriteEnd. * * This function updates the inode->i_size, conditionally marks the page * updated and carries out the actual write in case of partial page writes. * * Results: * Zero on succes, non-zero on error. * * Side effects: * None. * *----------------------------------------------------------------------------- */ static int HgfsDoWriteEnd(struct file *file, // IN: File we're writing to struct page *page, // IN: Page we're writing from unsigned pageFrom, // IN: Starting page offset unsigned pageTo, // IN: Ending page offset loff_t writeTo, // IN: File position to write to unsigned copied) // IN: Number of bytes copied to the page { HgfsHandle handle; struct inode *inode; loff_t currentFileSize; loff_t offset; ASSERT(file); ASSERT(page); inode = page->mapping->host; currentFileSize = compat_i_size_read(inode); offset = (loff_t)page->index << PAGE_CACHE_SHIFT; /* See comment in HgfsPrepareWrite. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 10) kunmap(page); #endif if (writeTo > currentFileSize) { compat_i_size_write(inode, writeTo); } /* We wrote a complete page, so it is up to date. */ if (copied == PAGE_CACHE_SIZE) { SetPageUptodate(page); } #ifdef HGFS_ENABLE_WRITEBACK /* * Check if this is a partial write to a new page, which was * initialized in HgfsDoWriteBegin. */ if ((offset >= currentFileSize) || ((pageFrom == 0) && (writeTo >= currentFileSize))) { SetPageUptodate(page); } /* * If the page is uptodate, then just mark it dirty and let * the page cache write it when it wants to. */ if (PageUptodate(page)) { set_page_dirty(page); return 0; } #endif /* * We've recieved a partial write to page that is not uptodate, so * do the write now while the page is still locked. Another * alternative would be to read the page in HgfsDoWriteBegin, which * would make it uptodate (ie a complete cached page). */ handle = FILE_GET_FI_P(file)->handle; LOG(6, (KERN_DEBUG "VMware hgfs: %s: writing to handle %u\n", __FUNCTION__, handle)); return HgfsDoWritepage(handle, page, pageFrom, pageTo); } #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28) /* *----------------------------------------------------------------------------- * * HgfsCommitWrite -- * * This function is the more common write path for HGFS, called from * generic_file_buffered_write. It is much simpler for us than * HgfsWritepage above: the caller has obtained a reference to the page * and will unlock it when we're done. And we don't need to worry about * properly marking the writeback bit, either. See mm/filemap.c in the * kernel for details about how we are called. * * Results: * Zero on succes, non-zero on error. * * Side effects: * None. * *----------------------------------------------------------------------------- */ static int HgfsCommitWrite(struct file *file, // IN: File to write struct page *page, // IN: Page to write from unsigned pageFrom, // IN: Starting page offset unsigned pageTo) // IN: Ending page offset { loff_t offset; loff_t writeTo; unsigned copied; ASSERT(page); ASSERT(file); offset = (loff_t)page->index << PAGE_CACHE_SHIFT; writeTo = offset + pageTo; copied = pageTo - pageFrom; return HgfsDoWriteEnd(file, page, pageFrom, pageTo, writeTo, copied); } #else /* *----------------------------------------------------------------------------- * * HgfsWriteEnd -- * * This function is the more common write path for HGFS, called from * generic_file_buffered_write. It is much simpler for us than * HgfsWritepage above: write_begin has obtained a reference to the page * and we will unlock it when we're done. And we don't need to worry about * properly marking the writeback bit, either. See mm/filemap.c in the * kernel for details about how we are called. * * This function should unlock the page and reduce the refcount. * * Results: * Number of bytes written or negative error * * Side effects: * Unlocks the page and drops the reference. * *----------------------------------------------------------------------------- */ static int HgfsWriteEnd(struct file *file, // IN: File to write struct address_space *mapping, // IN: Mapping loff_t pos, // IN: File position unsigned len, // IN: len passed from write_begin unsigned copied, // IN: Number of actually copied bytes struct page *page, // IN: Page to write from void *clientData) // IN: From write_begin, unused. { unsigned pageFrom = pos & (PAGE_CACHE_SIZE - 1); unsigned pageTo = pageFrom + copied; loff_t writeTo = pos + copied; int ret; ASSERT(file); ASSERT(mapping); ASSERT(page); ret = HgfsDoWriteEnd(file, page, pageFrom, pageTo, writeTo, copied); if (ret == 0) { ret = copied; } compat_unlock_page(page); page_cache_release(page); return ret; } #endif vmhgfs-only/request.c0000444000000000000000000001560312025726746013677 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ /* * request.c -- * * Functions dealing with the creation, deletion, and sending of HGFS * requests are defined here. */ /* Must come before any kernel header file. */ #include "driver-config.h" #include #include #include #include "compat_kernel.h" #include "compat_sched.h" #include "compat_semaphore.h" #include "compat_slab.h" #include "compat_spinlock.h" #include "module.h" #include "request.h" #include "fsutil.h" #include "vm_assert.h" static int HgfsWaitRequestReply(HgfsReq *req); /* * Private function implementations. */ /* *---------------------------------------------------------------------- * * HgfsWaitRequestReply -- * * Wait for the reply to a request that we sent. * * Results: * Returns zero when the answer has been received, -ERESTARTSYS if * interrupted, or -EPROTO if there was a backdoor error. It is important * that -ERESTARTSYS be returned in the event of a signal getting caught, * because calling functions test the return value to determine * whether or not to free the request object. * * Side effects: * None * *---------------------------------------------------------------------- */ static int HgfsWaitRequestReply(HgfsReq *req) // IN/OUT: Request object { int err = 0; long timeleft; ASSERT(req); if (!req) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsWaitRequestReply: null req\n")); return -EINVAL; } timeleft = wait_event_timeout(req->queue, (req->state == HGFS_REQ_STATE_COMPLETED || req->state == HGFS_REQ_STATE_ERROR), HGFS_REQUEST_TIMEOUT); /* * Did we time out? If so, abandon the request. We have to be careful, * because a timeout means that the request is still on a list somewhere. */ if (timeleft == 0) { spin_lock(&hgfsBigLock); if (!list_empty(&req->list)) { list_del_init(&req->list); } spin_unlock(&hgfsBigLock); /* * Notice that we're completely ignoring any pending signals. That's * because the request timed out; it was not interrupted. There's no * point in having the client retry the syscall (through -ERESTARTSYS) if * it wasn't actually interrupted. */ err = -EIO; } else if (req->state == HGFS_REQ_STATE_ERROR) { /* * If the backdoor exploded, let's modify the return value so the client * knows about it. We only care about this if we didn't timeout. */ err = -EPROTO; } LOG(8, (KERN_DEBUG "VMware hgfs: HgfsWaitRequestReply: request finished, " "code %d\n", err)); return err; } /* * Public function implementations. */ /* *---------------------------------------------------------------------- * * HgfsGetNewRequest -- * * Get a new request structure off the free list and initialize it. * * Results: * On success the new struct is returned with all fields * initialized. Returns NULL on failure. * * Side effects: * None * *---------------------------------------------------------------------- */ HgfsReq * HgfsGetNewRequest(void) { HgfsReq *req = NULL; req = kmem_cache_alloc(hgfsReqCache, GFP_KERNEL); if (req == NULL) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsGetNewRequest: " "can't allocate memory\n")); return NULL; } init_waitqueue_head(&req->queue); req->payloadSize = 0; req->state = HGFS_REQ_STATE_ALLOCATED; /* Setup the packet prefix. */ memcpy(req->packet, HGFS_SYNC_REQREP_CLIENT_CMD, HGFS_SYNC_REQREP_CLIENT_CMD_LEN); /* Atomically increment counter and set ID. */ spin_lock(&hgfsBigLock); req->id = hgfsIdCounter++; spin_unlock(&hgfsBigLock); return req; } /* *---------------------------------------------------------------------- * * HgfsSendRequest -- * * Add an HGFS request to the request queue, wake up the backdoor * handler, and wait for the reply. * * Results: * Returns zero on success, -ERESTARTSYS if interrupted (this value will * be returned by HgfsWaitRequestReply), negative error * otherwise. Callers use the -ERESTARTSYS return value to determine * whether they should free the request object before exiting. * * Side effects: * None * *---------------------------------------------------------------------- */ int HgfsSendRequest(HgfsReq *req) // IN/OUT: Outgoing request { int error; ASSERT(req); ASSERT(req->payloadSize <= HGFS_PACKET_MAX); req->state = HGFS_REQ_STATE_UNSENT; LOG(8, (KERN_DEBUG "VMware hgfs: HgfsSendRequest: Sending request id %d\n", req->id)); /* * Add the request to the queue, wake up the backdoor handler thread, and * wait for a reply. */ spin_lock(&hgfsBigLock); list_add_tail(&req->list, &hgfsReqsUnsent); spin_unlock(&hgfsBigLock); set_bit(HGFS_REQ_THREAD_SEND, &hgfsReqThreadFlags); wake_up_interruptible(&hgfsReqThreadWait); error = HgfsWaitRequestReply(req); return error; } /* *---------------------------------------------------------------------- * * HgfsFreeRequest -- * * Free an HGFS request. * * Results: * None * * Side effects: * None * *---------------------------------------------------------------------- */ void HgfsFreeRequest(HgfsReq *req) // IN: Request to free { ASSERT(hgfsReqCache); /* Atomically decrement counter. */ spin_lock(&hgfsBigLock); hgfsIdCounter--; spin_unlock(&hgfsBigLock); if (req != NULL) { kmem_cache_free(hgfsReqCache, req); } } /* *---------------------------------------------------------------------- * * HgfsReplyStatus -- * * Return reply status. * * Results: * Returns reply status as per the protocol. * XXX: Needs changes when vmci headers are added. * * Side effects: * None * *---------------------------------------------------------------------- */ HgfsStatus HgfsReplyStatus(HgfsReq *req) // IN { HgfsReply *rep; rep = (HgfsReply *)(HGFS_REQ_PAYLOAD(req)); return rep->status; } vmhgfs-only/request.h0000444000000000000000000000756712025726746013716 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ /* * request.h -- * * Functions dealing with the creation, deletion, and sending of HGFS * requests are defined here. */ #ifndef _HGFS_DRIVER_REQUEST_H_ #define _HGFS_DRIVER_REQUEST_H_ /* Must come before any kernel header file. */ #include "driver-config.h" #include #include "compat_sched.h" #include "compat_spinlock.h" #include "compat_wait.h" #include "hgfs.h" /* For common HGFS definitions. */ #include "vm_basic_types.h" /* Macros for accessing the payload portion of the HGFS request packet. */ #define HGFS_REQ_PAYLOAD(hgfsReq) ((hgfsReq)->packet + HGFS_CLIENT_CMD_LEN) /* XXX: Needs change when VMCI is supported. */ #define HGFS_REQ_PAYLOAD_V3(hgfsReq) (HGFS_REQ_PAYLOAD(hgfsReq) + sizeof(HgfsRequest)) #define HGFS_REP_PAYLOAD_V3(hgfsRep) (HGFS_REQ_PAYLOAD(hgfsRep) + sizeof(HgfsReply)) /* * HGFS_REQ_STATE_ALLOCATED: * The filesystem half has allocated the request from the slab * allocator. The request is not on any list. * * HGFS_REQ_STATE_UNSENT: * The filesystem half of the driver has filled in the request fields * and placed the request in the global unsent list. It is now the * backdoor handler's responsibility to submit this request to * the backdoor. Requests in this state are on the global unsent list. * * HGFS_REQ_STATE_SENT: * The backdoor handler has sent the packet, but the reply will arrive * asynchronously. The request is now on the sent list, and whenever * the reply arrives, the backdoor handler will remove the request from * the sent list and stuff the reply into the request's packet buffer. * * HGFS_REQ_STATE_ERROR: * The backdoor handler encountered an error while sending the request * or getting the reply. The filesystem half of the driver should * free the request. Requests in this state are not on any list. * * HGFS_REQ_STATE_COMPLETED: * The backdoor handler sent the request and received a reply. The reply * is stuffed in the request's packet buffer. Requests in this state * are not on any list. */ typedef enum { HGFS_REQ_STATE_ALLOCATED, HGFS_REQ_STATE_UNSENT, HGFS_REQ_STATE_SENT, HGFS_REQ_STATE_ERROR, HGFS_REQ_STATE_COMPLETED, } HgfsState; /* * A request to be sent to the user process. */ typedef struct HgfsReq { /* Links to place the object on various lists. */ struct list_head list; /* * When clients wait for the reply to a request, they'll wait on this * wait queue. */ wait_queue_head_t queue; /* Current state of the request. */ HgfsState state; /* ID of this request */ uint32 id; /* Total size of the payload.*/ size_t payloadSize; /* * Packet of data, for both incoming and outgoing messages. * Include room for the command. */ char packet[HGFS_PACKET_MAX + HGFS_CLIENT_CMD_LEN]; } HgfsReq; /* Public functions (with respect to the entire module). */ HgfsReq *HgfsGetNewRequest(void); int HgfsSendRequest(HgfsReq *req); void HgfsFreeRequest(HgfsReq *req); HgfsStatus HgfsReplyStatus(HgfsReq *req); // IN #endif // _HGFS_DRIVER_REQUEST_H_ vmhgfs-only/stubs.c0000444000000000000000000000370112025726746013343 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ /* * stubs.c * * Contains stubs and helper functions. */ /* Must come before any kernel header file. */ #include "driver-config.h" #include "kernelStubs.h" #include "module.h" #include "vm_assert.h" /* *---------------------------------------------------------------------- * * Debug -- * * If debugging is enabled, output debug information. * * Result * None * * Side-effects * None * *---------------------------------------------------------------------- */ void Debug(char const *fmt, // IN: Format string ...) // IN: Arguments { va_list args; int numBytes; static char out[128]; va_start(args, fmt); numBytes = Str_Vsnprintf(out, sizeof out, fmt, args); va_end(args); if (numBytes > 0) { LOG(6, (KERN_DEBUG "VMware hgfs: %s", out)); } } /* *---------------------------------------------------------------------- * * Log -- * * Needs to be defined. * * Result * None * * Side-effects * None * *---------------------------------------------------------------------- */ void Log(const char *string, ...) { // do nothing. } vmhgfs-only/super.c0000444000000000000000000003074412025726746013350 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ /* * super.c -- * * Superblock operations for the filesystem portion of the vmhgfs driver. */ /* Must come before any kernel header file. */ #include "driver-config.h" #include #include "compat_fs.h" #include "compat_statfs.h" #include "compat_kernel.h" #include "compat_slab.h" #include "compat_sched.h" #include "compat_version.h" #include "hgfsProto.h" #include "escBitvector.h" #include "cpName.h" #include "hgfsUtil.h" #include "request.h" #include "fsutil.h" #include "staticEscape.h" #include "hgfsDevLinux.h" #include "module.h" #include "vm_assert.h" /* Hgfs filesystem superblock operations */ #ifdef VMW_EMBED_INODE static struct inode *HgfsAllocInode(struct super_block *sb); static void HgfsDestroyInode(struct inode *inode); #endif #ifndef VMW_USE_IGET_LOCKED static void HgfsReadInode(struct inode *inode); #endif static void HgfsClearInode(struct inode *inode); static void HgfsPutSuper(struct super_block *sb); #if defined(VMW_STATFS_2618) static int HgfsStatfs(struct dentry *dentry, struct compat_kstatfs *stat); #else static int HgfsStatfs(struct super_block *sb, struct compat_kstatfs *stat); #endif struct super_operations HgfsSuperOperations = { #ifdef VMW_EMBED_INODE .alloc_inode = HgfsAllocInode, .destroy_inode = HgfsDestroyInode, #endif #ifndef VMW_USE_IGET_LOCKED .read_inode = HgfsReadInode, #endif .clear_inode = HgfsClearInode, .put_super = HgfsPutSuper, .statfs = HgfsStatfs, }; #ifdef VMW_EMBED_INODE /* *----------------------------------------------------------------------------- * * HgfsAllocInode -- * * Hgfs superblock 'alloc_inode' method. Called by the kernel to allocate * a new inode struct. We use this VFS method instead of read_inode because * we want to control both how we allocate and how we fill in the inode. * * Results: * Non-null: A valid inode. * null: Error in inode allocation. * * Side effects: * Allocates memory. * *----------------------------------------------------------------------------- */ static struct inode * HgfsAllocInode(struct super_block *sb) // IN: Superblock for the inode { HgfsInodeInfo *iinfo; iinfo = kmem_cache_alloc(hgfsInodeCache, GFP_KERNEL); if (!iinfo) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsAllocInode: " "can't allocate memory\n")); return NULL; } return &iinfo->inode; } /* *----------------------------------------------------------------------------- * * HgfsDestroyInode -- * * Hgfs superblock 'destroy_inode' method. Called by the kernel when it * deallocates an inode. We use this method instead of clear_inode because * we want to control both how we deallocate and how we clear the inode. * * Results: * None * * Side effects: * Frees memory associated with inode. * *----------------------------------------------------------------------------- */ static void HgfsDestroyInode(struct inode *inode) // IN: The VFS inode { kmem_cache_free(hgfsInodeCache, INODE_GET_II_P(inode)); } #endif #ifndef VMW_USE_IGET_LOCKED /* *----------------------------------------------------------------------------- * * HgfsReadInode -- * * Hgfs superblock 'read_inode' method. Called by the kernel to fill in a * VFS inode, given its hgfs inode number. Needed by iget(). * * Results: * None * * Side effects: * None * *----------------------------------------------------------------------------- */ static void HgfsReadInode(struct inode *inode) // IN/OUT: VFS inode to fill in { HgfsDoReadInode(inode); } #endif /* *----------------------------------------------------------------------------- * * HgfsClearInode -- * * Hgfs superblock 'clear_inode' method. Called by the kernel when it is * about to destroy a VFS inode. * * Results: * None * * Side effects: * None * *----------------------------------------------------------------------------- */ static void HgfsClearInode(struct inode *inode) // IN: The VFS inode { #ifdef VMW_EMBED_INODE /* Do nothing. HgfsDestroyInode will do the dirty work. */ #else HgfsInodeInfo *iinfo; ASSERT(inode); /* The HGFS inode information may be partially constructed --hpreg */ iinfo = INODE_GET_II_P(inode); if (iinfo) { kmem_cache_free(hgfsInodeCache, iinfo); } #endif } /* *----------------------------------------------------------------------------- * * HgfsPutSuper -- * * Hgfs superblock 'put_super' method. Called after a umount(2) of the * filesystem succeeds. * * Results: * None * * Side effects: * None * *----------------------------------------------------------------------------- */ static void HgfsPutSuper(struct super_block *sb) // IN: The superblock { HgfsSuperInfo *si; ASSERT(sb); LOG(6, (KERN_DEBUG "VMware hgfs: HgfsPutSuper: was called\n")); si = HGFS_SB_TO_COMMON(sb); kfree(si->shareName); kfree(si); } /* *---------------------------------------------------------------------- * * HgfsPackQueryVolumeRequest -- * * Setup the query volume request, depending on the op version. * * Results: * Returns zero on success, or negative error on failure. * * Side effects: * None * *---------------------------------------------------------------------- */ static int HgfsPackQueryVolumeRequest(struct dentry *dentry, // IN: File pointer for this open HgfsOp opUsed, // IN: Op to be used. HgfsReq *req) // IN/OUT: Packet to write into { char *name; uint32 *nameLength; size_t requestSize; int result; ASSERT(dentry); ASSERT(req); switch (opUsed) { case HGFS_OP_QUERY_VOLUME_INFO_V3: { HgfsRequest *requestHeader; HgfsRequestQueryVolumeV3 *requestV3; requestHeader = (HgfsRequest *)(HGFS_REQ_PAYLOAD(req)); requestHeader->op = opUsed; requestHeader->id = req->id; requestV3 = (HgfsRequestQueryVolumeV3 *)HGFS_REQ_PAYLOAD_V3(req); /* We'll use these later. */ name = requestV3->fileName.name; nameLength = &requestV3->fileName.length; requestV3->fileName.flags = 0; requestV3->fileName.fid = HGFS_INVALID_HANDLE; requestV3->fileName.caseType = HGFS_FILE_NAME_CASE_SENSITIVE; requestV3->reserved = 0; requestSize = HGFS_REQ_PAYLOAD_SIZE_V3(requestV3); break; } case HGFS_OP_QUERY_VOLUME_INFO: { HgfsRequestQueryVolume *request; request = (HgfsRequestQueryVolume *)(HGFS_REQ_PAYLOAD(req)); request->header.op = opUsed; request->header.id = req->id; /* We'll use these later. */ name = request->fileName.name; nameLength = &request->fileName.length; requestSize = sizeof *request; break; } default: LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPackQueryVolumeRequest: unexpected " "OP type encountered\n")); return -EPROTO; } /* Build full name to send to server. */ if (HgfsBuildPath(name, HGFS_PACKET_MAX - (requestSize - 1), dentry) < 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPackQueryVolumeRequest: build path failed\n")); return -EINVAL; } LOG(6, (KERN_DEBUG "VMware hgfs: HgfsPackQueryVolumeRequest: opening \"%s\"\n", name)); /* Convert to CP name. */ result = CPName_ConvertTo(name, HGFS_PACKET_MAX - (requestSize - 1), name); if (result < 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPackQueryVolumeRequest: CP conversion failed\n")); return -EINVAL; } /* Unescape the CP name. */ result = HgfsUnescapeBuffer(name, result); *nameLength = (uint32) result; req->payloadSize = requestSize + result; return 0; } /* *----------------------------------------------------------------------------- * * HgfsStatfs -- * * Hgfs superblock 'statfs' method. Called when statfs(2) is invoked on the * filesystem. * * Results: * 0 on success * error < 0 on failure * * Side effects: * None * *----------------------------------------------------------------------------- */ #if defined(VMW_STATFS_2618) static int HgfsStatfs(struct dentry *dentry, // IN : The directory entry struct compat_kstatfs *stat) // OUT: Stat to fill in #else static int HgfsStatfs(struct super_block *sb, // IN : The superblock struct compat_kstatfs *stat) // OUT: Stat to fill in #endif { HgfsReq *req; int result = 0; struct dentry *dentryToUse; struct super_block *sbToUse; HgfsOp opUsed; HgfsStatus replyStatus; uint64 freeBytes; uint64 totalBytes; ASSERT(stat); #if defined(VMW_STATFS_2618) ASSERT(dentry); ASSERT(dentry->d_sb); dentryToUse = dentry; sbToUse = dentry->d_sb; #else ASSERT(sb); ASSERT(sb->s_root); dentryToUse = sb->s_root; sbToUse = sb; #endif LOG(6, (KERN_DEBUG "VMware hgfs: HgfsStatfs: was called\n")); memset(stat, 0, sizeof *stat); req = HgfsGetNewRequest(); if (!req) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsStatfs: out of memory while " "getting new request\n")); result = -ENOMEM; goto out; } retry: opUsed = atomic_read(&hgfsVersionQueryVolumeInfo); result = HgfsPackQueryVolumeRequest(dentryToUse, opUsed, req); if (result != 0) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsStatfs: error packing request\n")); goto out; } result = HgfsSendRequest(req); if (result == 0) { LOG(6, (KERN_DEBUG "VMware hgfs: HgfsStatfs: got reply\n")); replyStatus = HgfsReplyStatus(req); result = HgfsStatusConvertToLinux(replyStatus); /* * If the statfs succeeded on the server, copy the stats * into the kstatfs struct, otherwise return an error. */ switch (result) { case 0: stat->f_type = HGFS_SUPER_MAGIC; stat->f_bsize = sbToUse->s_blocksize; stat->f_namelen = PATH_MAX; if (opUsed == HGFS_OP_QUERY_VOLUME_INFO_V3) { totalBytes = ((HgfsReplyQueryVolumeV3 *)HGFS_REP_PAYLOAD_V3(req))->totalBytes; freeBytes = ((HgfsReplyQueryVolumeV3 *)HGFS_REP_PAYLOAD_V3(req))->freeBytes; } else { totalBytes = ((HgfsReplyQueryVolume *)HGFS_REQ_PAYLOAD(req))->totalBytes; freeBytes = ((HgfsReplyQueryVolume *)HGFS_REQ_PAYLOAD(req))->freeBytes; } stat->f_blocks = totalBytes >> sbToUse->s_blocksize_bits; stat->f_bfree = freeBytes >> sbToUse->s_blocksize_bits; stat->f_bavail = stat->f_bfree; break; case -EPERM: /* * We're cheating! This will cause statfs will return success. * We're doing this because an old server will complain when it gets * a statfs on a per-share mount. Rather than have 'df' spit an * error, let's just return all zeroes. */ result = 0; break; case -EPROTO: /* Retry with older version(s). Set globally. */ if (opUsed == HGFS_OP_QUERY_VOLUME_INFO_V3) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsStatfs: Version 3 not " "supported. Falling back to version 1.\n")); atomic_set(&hgfsVersionQueryVolumeInfo, HGFS_OP_QUERY_VOLUME_INFO); goto retry; } break; default: break; } } else if (result == -EIO) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsStatfs: timed out\n")); } else if (result == -EPROTO) { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsStatfs: server returned error: " "%d\n", result)); } else { LOG(4, (KERN_DEBUG "VMware hgfs: HgfsStatfs: unknown error: %d\n", result)); } out: HgfsFreeRequest(req); return result; } vmhgfs-only/vmhgfs_version.h0000444000000000000000000000222012025726746015242 0ustar rootroot/********************************************************* * Copyright (C) 2007 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 * *********************************************************/ /* * vmhgfs_version.h -- * * Version definitions for the Linux vmhgfs driver. */ #ifndef _VMHGFS_VERSION_H_ #define _VMHGFS_VERSION_H_ #define VMHGFS_DRIVER_VERSION 1.4.1.1 #define VMHGFS_DRIVER_VERSION_COMMAS 1,4,1,1 #define VMHGFS_DRIVER_VERSION_STRING "1.4.1.1" #endif /* _VMHGFS_VERSION_H_ */ vmhgfs-only/backdoor.c0000444000000000000000000001435712025726746014000 0ustar rootroot/********************************************************* * Copyright (C) 1999 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 * *********************************************************/ /* * backdoor.c -- * * First layer of the internal communication channel between guest * applications and vmware * * This is the backdoor. By using special ports of the virtual I/O space, * and the virtual CPU registers, a guest application can send a * synchroneous basic request to vmware, and vmware can reply to it. */ #ifdef __cplusplus extern "C" { #endif #if defined(__KERNEL__) || defined(_KERNEL) #else # include "debug.h" #endif #include "backdoor_def.h" #include "backdoor.h" #include "backdoorInt.h" #if defined(BACKDOOR_DEBUG) && defined(USERLEVEL) # include # define BACKDOOR_LOG(args) Debug args # define BACKDOOR_LOG_PROTO_STRUCT(x) BackdoorPrintProtoStruct((x)) # define BACKDOOR_LOG_HB_PROTO_STRUCT(x) BackdoorPrintHbProtoStruct((x)) /* *---------------------------------------------------------------------------- * * BackdoorPrintProtoStruct -- * BackdoorPrintHbProtoStruct -- * * Print the contents of the specified backdoor protocol structure via * printf. * * Results: * None. * * Side effects: * Output to stdout. * *---------------------------------------------------------------------------- */ void BackdoorPrintProtoStruct(Backdoor_proto *myBp) { Debug("magic 0x%08x, command %d, size %"FMTSZ"u, port %d\n", myBp->in.ax.word, myBp->in.cx.halfs.low, myBp->in.size, myBp->in.dx.halfs.low); #ifndef VM_X86_64 Debug("ax %#x, " "bx %#x, " "cx %#x, " "dx %#x, " "si %#x, " "di %#x\n", myBp->out.ax.word, myBp->out.bx.word, myBp->out.cx.word, myBp->out.dx.word, myBp->out.si.word, myBp->out.di.word); #else Debug("ax %#"FMT64"x, " "bx %#"FMT64"x, " "cx %#"FMT64"x, " "dx %#"FMT64"x, " "si %#"FMT64"x, " "di %#"FMT64"x\n", myBp->out.ax.quad, myBp->out.bx.quad, myBp->out.cx.quad, myBp->out.dx.quad, myBp->out.si.quad, myBp->out.di.quad); #endif } void BackdoorPrintHbProtoStruct(Backdoor_proto_hb *myBp) { Debug("magic 0x%08x, command %d, size %"FMTSZ"u, port %d, " "srcAddr %"FMTSZ"u, dstAddr %"FMTSZ"u\n", myBp->in.ax.word, myBp->in.bx.halfs.low, myBp->in.size, myBp->in.dx.halfs.low, myBp->in.srcAddr, myBp->in.dstAddr); #ifndef VM_X86_64 Debug("ax %#x, " "bx %#x, " "cx %#x, " "dx %#x, " "si %#x, " "di %#x, " "bp %#x\n", myBp->out.ax.word, myBp->out.bx.word, myBp->out.cx.word, myBp->out.dx.word, myBp->out.si.word, myBp->out.di.word, myBp->out.bp.word); #else Debug("ax %#"FMT64"x, " "bx %#"FMT64"x, " "cx %#"FMT64"x, " "dx %#"FMT64"x, " "si %#"FMT64"x, " "di %#"FMT64"x, " "bp %#"FMT64"x\n", myBp->out.ax.quad, myBp->out.bx.quad, myBp->out.cx.quad, myBp->out.dx.quad, myBp->out.si.quad, myBp->out.di.quad, myBp->out.bp.quad); #endif } #else # define BACKDOOR_LOG(args) # define BACKDOOR_LOG_PROTO_STRUCT(x) # define BACKDOOR_LOG_HB_PROTO_STRUCT(x) #endif /* *----------------------------------------------------------------------------- * * Backdoor -- * * Send a low-bandwidth basic request (16 bytes) to vmware, and return its * reply (24 bytes). * * Result: * None * * Side-effects: * None * *----------------------------------------------------------------------------- */ void Backdoor(Backdoor_proto *myBp) // IN/OUT { ASSERT(myBp); myBp->in.ax.word = BDOOR_MAGIC; myBp->in.dx.halfs.low = BDOOR_PORT; BACKDOOR_LOG(("Backdoor: before ")); BACKDOOR_LOG_PROTO_STRUCT(myBp); Backdoor_InOut(myBp); BACKDOOR_LOG(("Backdoor: after ")); BACKDOOR_LOG_PROTO_STRUCT(myBp); } /* *----------------------------------------------------------------------------- * * Backdoor_HbOut -- * * Send a high-bandwidth basic request to vmware, and return its * reply. * * Result: * The host-side response is returned via the IN/OUT parameter. * * Side-effects: * Pokes the high-bandwidth backdoor. * *----------------------------------------------------------------------------- */ void Backdoor_HbOut(Backdoor_proto_hb *myBp) // IN/OUT { ASSERT(myBp); myBp->in.ax.word = BDOOR_MAGIC; myBp->in.dx.halfs.low = BDOORHB_PORT; BACKDOOR_LOG(("Backdoor_HbOut: before ")); BACKDOOR_LOG_HB_PROTO_STRUCT(myBp); BackdoorHbOut(myBp); BACKDOOR_LOG(("Backdoor_HbOut: after ")); BACKDOOR_LOG_HB_PROTO_STRUCT(myBp); } /* *----------------------------------------------------------------------------- * * Backdoor_HbIn -- * * Send a basic request to vmware, and return its high-bandwidth * reply * * Result: * Host-side response returned via the IN/OUT parameter. * * Side-effects: * Pokes the high-bandwidth backdoor. * *----------------------------------------------------------------------------- */ void Backdoor_HbIn(Backdoor_proto_hb *myBp) // IN/OUT { ASSERT(myBp); myBp->in.ax.word = BDOOR_MAGIC; myBp->in.dx.halfs.low = BDOORHB_PORT; BACKDOOR_LOG(("Backdoor_HbIn: before ")); BACKDOOR_LOG_HB_PROTO_STRUCT(myBp); BackdoorHbIn(myBp); BACKDOOR_LOG(("Backdoor_HbIn: after ")); BACKDOOR_LOG_HB_PROTO_STRUCT(myBp); } #ifdef __cplusplus } #endif vmhgfs-only/backdoorGcc32.c0000444000000000000000000001403612025726746014554 0ustar rootroot/********************************************************* * Copyright (C) 2005 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 * *********************************************************/ /* * backdoorGcc32.c -- * * Implements the real work for guest-side backdoor for GCC, 32-bit * target (supports inline ASM, GAS syntax). The asm sections are marked * volatile since vmware can change the registers content without the * compiler knowing it. * * XXX * I tried to write this more cleanly, but: * - There is no way to specify an "ebp" constraint * - "ebp" is ignored when specified as cloberred register * - gas barfs when there is more than 10 operands * - gas 2.7.2.3, depending on the order of the operands, can * mis-assemble without any warning * --hpreg * * Note that the problems with gas noted above might longer be relevant * now that we've upgraded most of our compiler versions. * --rrdharan */ #ifdef __cplusplus extern "C" { #endif #include "backdoor.h" #include "backdoorInt.h" /* *---------------------------------------------------------------------------- * * Backdoor_InOut -- * * Send a low-bandwidth basic request (16 bytes) to vmware, and return its * reply (24 bytes). * * Results: * Host-side response returned in bp IN/OUT parameter. * * Side effects: * Pokes the backdoor. * *---------------------------------------------------------------------------- */ void Backdoor_InOut(Backdoor_proto *myBp) // IN/OUT { uint32 dummy; __asm__ __volatile__( #ifdef __PIC__ "pushl %%ebx" "\n\t" #endif "pushl %%eax" "\n\t" "movl 20(%%eax), %%edi" "\n\t" "movl 16(%%eax), %%esi" "\n\t" "movl 12(%%eax), %%edx" "\n\t" "movl 8(%%eax), %%ecx" "\n\t" "movl 4(%%eax), %%ebx" "\n\t" "movl (%%eax), %%eax" "\n\t" "inl %%dx, %%eax" "\n\t" "xchgl %%eax, (%%esp)" "\n\t" "movl %%edi, 20(%%eax)" "\n\t" "movl %%esi, 16(%%eax)" "\n\t" "movl %%edx, 12(%%eax)" "\n\t" "movl %%ecx, 8(%%eax)" "\n\t" "movl %%ebx, 4(%%eax)" "\n\t" "popl (%%eax)" "\n\t" #ifdef __PIC__ "popl %%ebx" "\n\t" #endif : "=a" (dummy) : "0" (myBp) /* * vmware can modify the whole VM state without the compiler knowing * it. So far it does not modify EFLAGS. --hpreg */ : #ifndef __PIC__ "ebx", #endif "ecx", "edx", "esi", "edi", "memory" ); } /* *----------------------------------------------------------------------------- * * BackdoorHbIn -- * BackdoorHbOut -- * * Send a high-bandwidth basic request to vmware, and return its * reply. * * Results: * Host-side response returned in bp IN/OUT parameter. * * Side-effects: * Pokes the high-bandwidth backdoor port. * *----------------------------------------------------------------------------- */ void BackdoorHbIn(Backdoor_proto_hb *myBp) // IN/OUT { uint32 dummy; __asm__ __volatile__( #ifdef __PIC__ "pushl %%ebx" "\n\t" #endif "pushl %%ebp" "\n\t" "pushl %%eax" "\n\t" "movl 24(%%eax), %%ebp" "\n\t" "movl 20(%%eax), %%edi" "\n\t" "movl 16(%%eax), %%esi" "\n\t" "movl 12(%%eax), %%edx" "\n\t" "movl 8(%%eax), %%ecx" "\n\t" "movl 4(%%eax), %%ebx" "\n\t" "movl (%%eax), %%eax" "\n\t" "cld" "\n\t" "rep; insb" "\n\t" "xchgl %%eax, (%%esp)" "\n\t" "movl %%ebp, 24(%%eax)" "\n\t" "movl %%edi, 20(%%eax)" "\n\t" "movl %%esi, 16(%%eax)" "\n\t" "movl %%edx, 12(%%eax)" "\n\t" "movl %%ecx, 8(%%eax)" "\n\t" "movl %%ebx, 4(%%eax)" "\n\t" "popl (%%eax)" "\n\t" "popl %%ebp" "\n\t" #ifdef __PIC__ "popl %%ebx" "\n\t" #endif : "=a" (dummy) : "0" (myBp) /* * vmware can modify the whole VM state without the compiler knowing * it. --hpreg */ : #ifndef __PIC__ "ebx", #endif "ecx", "edx", "esi", "edi", "memory", "cc" ); } void BackdoorHbOut(Backdoor_proto_hb *myBp) // IN/OUT { uint32 dummy; __asm__ __volatile__( #ifdef __PIC__ "pushl %%ebx" "\n\t" #endif "pushl %%ebp" "\n\t" "pushl %%eax" "\n\t" "movl 24(%%eax), %%ebp" "\n\t" "movl 20(%%eax), %%edi" "\n\t" "movl 16(%%eax), %%esi" "\n\t" "movl 12(%%eax), %%edx" "\n\t" "movl 8(%%eax), %%ecx" "\n\t" "movl 4(%%eax), %%ebx" "\n\t" "movl (%%eax), %%eax" "\n\t" "cld" "\n\t" "rep; outsb" "\n\t" "xchgl %%eax, (%%esp)" "\n\t" "movl %%ebp, 24(%%eax)" "\n\t" "movl %%edi, 20(%%eax)" "\n\t" "movl %%esi, 16(%%eax)" "\n\t" "movl %%edx, 12(%%eax)" "\n\t" "movl %%ecx, 8(%%eax)" "\n\t" "movl %%ebx, 4(%%eax)" "\n\t" "popl (%%eax)" "\n\t" "popl %%ebp" "\n\t" #ifdef __PIC__ "popl %%ebx" "\n\t" #endif : "=a" (dummy) : "0" (myBp) : #ifndef __PIC__ "ebx", #endif "ecx", "edx", "esi", "edi", "memory", "cc" ); } #ifdef __cplusplus } #endif vmhgfs-only/backdoorGcc64.c0000444000000000000000000001265612025726746014567 0ustar rootroot/********************************************************* * Copyright (C) 2005 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 * *********************************************************/ /* * backdoorGcc64.c -- * * Implements the real work for guest-side backdoor for GCC, 64-bit * target (supports inline ASM, GAS syntax). The asm sections are marked * volatile since vmware can change the registers content without the * compiler knowing it. * * See backdoorGCC32.c (from which this code was mostly copied) for * details on why the ASM is written this way. Also note that it might be * possible to write the asm blocks using the symbolic operand specifiers * in such a way that the same asm would generate correct code for both * 32-bit and 64-bit targets, but I'm too lazy to figure it all out. * --rrdharan */ #ifdef __cplusplus extern "C" { #endif #include "backdoor.h" #include "backdoorInt.h" /* *---------------------------------------------------------------------------- * * Backdoor_InOut -- * * Send a low-bandwidth basic request (16 bytes) to vmware, and return its * reply (24 bytes). * * Results: * Host-side response returned in bp IN/OUT parameter. * * Side effects: * Pokes the backdoor. * *---------------------------------------------------------------------------- */ void Backdoor_InOut(Backdoor_proto *myBp) // IN/OUT { uint64 dummy; __asm__ __volatile__( "pushq %%rax" "\n\t" "movq 40(%%rax), %%rdi" "\n\t" "movq 32(%%rax), %%rsi" "\n\t" "movq 24(%%rax), %%rdx" "\n\t" "movq 16(%%rax), %%rcx" "\n\t" "movq 8(%%rax), %%rbx" "\n\t" "movq (%%rax), %%rax" "\n\t" "inl %%dx, %%eax" "\n\t" /* NB: There is no inq instruction */ "xchgq %%rax, (%%rsp)" "\n\t" "movq %%rdi, 40(%%rax)" "\n\t" "movq %%rsi, 32(%%rax)" "\n\t" "movq %%rdx, 24(%%rax)" "\n\t" "movq %%rcx, 16(%%rax)" "\n\t" "movq %%rbx, 8(%%rax)" "\n\t" "popq (%%rax)" : "=a" (dummy) : "0" (myBp) /* * vmware can modify the whole VM state without the compiler knowing * it. So far it does not modify EFLAGS. --hpreg */ : "rbx", "rcx", "rdx", "rsi", "rdi", "memory" ); } /* *----------------------------------------------------------------------------- * * BackdoorHbIn -- * BackdoorHbOut -- * * Send a high-bandwidth basic request to vmware, and return its * reply. * * Results: * Host-side response returned in bp IN/OUT parameter. * * Side-effects: * Pokes the high-bandwidth backdoor port. * *----------------------------------------------------------------------------- */ void BackdoorHbIn(Backdoor_proto_hb *myBp) // IN/OUT { uint32 dummy; __asm__ __volatile__( "pushq %%rbp" "\n\t" "pushq %%rax" "\n\t" "movq 48(%%rax), %%rbp" "\n\t" "movq 40(%%rax), %%rdi" "\n\t" "movq 32(%%rax), %%rsi" "\n\t" "movq 24(%%rax), %%rdx" "\n\t" "movq 16(%%rax), %%rcx" "\n\t" "movq 8(%%rax), %%rbx" "\n\t" "movq (%%rax), %%rax" "\n\t" "cld" "\n\t" "rep; insb" "\n\t" "xchgq %%rax, (%%rsp)" "\n\t" "movq %%rbp, 48(%%rax)" "\n\t" "movq %%rdi, 40(%%rax)" "\n\t" "movq %%rsi, 32(%%rax)" "\n\t" "movq %%rdx, 24(%%rax)" "\n\t" "movq %%rcx, 16(%%rax)" "\n\t" "movq %%rbx, 8(%%rax)" "\n\t" "popq (%%rax)" "\n\t" "popq %%rbp" : "=a" (dummy) : "0" (myBp) /* * vmware can modify the whole VM state without the compiler knowing * it. --hpreg */ : "rbx", "rcx", "rdx", "rsi", "rdi", "memory", "cc" ); } void BackdoorHbOut(Backdoor_proto_hb *myBp) // IN/OUT { uint64 dummy; __asm__ __volatile__( "pushq %%rbp" "\n\t" "pushq %%rax" "\n\t" "movq 48(%%rax), %%rbp" "\n\t" "movq 40(%%rax), %%rdi" "\n\t" "movq 32(%%rax), %%rsi" "\n\t" "movq 24(%%rax), %%rdx" "\n\t" "movq 16(%%rax), %%rcx" "\n\t" "movq 8(%%rax), %%rbx" "\n\t" "movq (%%rax), %%rax" "\n\t" "cld" "\n\t" "rep; outsb" "\n\t" "xchgq %%rax, (%%rsp)" "\n\t" "movq %%rbp, 48(%%rax)" "\n\t" "movq %%rdi, 40(%%rax)" "\n\t" "movq %%rsi, 32(%%rax)" "\n\t" "movq %%rdx, 24(%%rax)" "\n\t" "movq %%rcx, 16(%%rax)" "\n\t" "movq %%rbx, 8(%%rax)" "\n\t" "popq (%%rax)" "\n\t" "popq %%rbp" : "=a" (dummy) : "0" (myBp) : "rbx", "rcx", "rdx", "rsi", "rdi", "memory", "cc" ); } #ifdef __cplusplus } #endif vmhgfs-only/backdoorInt.h0000444000000000000000000000201012025726746014437 0ustar rootroot/********************************************************* * Copyright (C) 2005 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 * *********************************************************/ /* * backdoorInt.h -- * * Internal function prototypes for the real backdoor work. */ void BackdoorHbIn(Backdoor_proto_hb *bp); void BackdoorHbOut(Backdoor_proto_hb *bp); vmhgfs-only/hgfsBd.c0000444000000000000000000002313412025726746013402 0ustar rootroot/********************************************************* * Copyright (C) 1998 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 * *********************************************************/ /* * hgfsBd.c -- * * Backdoor calls used by hgfs pserver. [bac] */ #if defined(__KERNEL__) || defined(_KERNEL) || defined(KERNEL) # include "kernelStubs.h" #else # include # include # include # include # include "str.h" // for Str_Strcpy # include "debug.h" #endif #include "vm_assert.h" #include "rpcout.h" #include "hgfs.h" // for common HGFS definitions #include "hgfsBd.h" /* *----------------------------------------------------------------------------- * * HgfsBdGetBufInt -- * * Allocates a buffer to send a hgfs request in. This can be either a * HGFS_PACKET_MAX or HGFS_LARGE_PACKET_MAX size buffer depending on the * external funciton called. * * Results: * Pointer to a buffer that has the correct backdoor command prefix for * sending hgfs requests over the backdoor. * NULL on failure (not enough memory). * * Side effects: * None. * *----------------------------------------------------------------------------- */ static char * HgfsBdGetBufInt(size_t bufSize) { /* * Allocate a buffer that is large enough for an HGFS packet and the * synchronous HGFS command, write the command, and return a pointer that * points into the buffer, after the command. */ size_t len = bufSize + HGFS_SYNC_REQREP_CLIENT_CMD_LEN; char *buf = (char*) calloc(sizeof(char), len); if (!buf) { Debug("HgfsBd_GetBuf: Failed to allocate a bd buffer\n"); return NULL; } Str_Strcpy(buf, HGFS_SYNC_REQREP_CLIENT_CMD, len); return buf + HGFS_SYNC_REQREP_CLIENT_CMD_LEN; } /* *----------------------------------------------------------------------------- * * HgfsBd_GetBuf -- * * Get a buffer of size HGFS_PACKET_MAX to send hgfs requests in. * * Results: * See HgfsBdGetBufInt. * * Side effects: * Allocates memory that must be freed with a call to HgfsBd_PutBuf. * *----------------------------------------------------------------------------- */ char * HgfsBd_GetBuf(void) { return HgfsBdGetBufInt(HGFS_PACKET_MAX); } /* *----------------------------------------------------------------------------- * * HgfsBd_GetLargeBuf -- * * Get a buffer of size HGFS_LARGE_PACKET_MAX to send hgfs requests in. * * Results: * See HgfsBdGetBufInt. * * Side effects: * Allocates memory that must be freed with a call to HgfsBd_PutBuf. * *----------------------------------------------------------------------------- */ char * HgfsBd_GetLargeBuf(void) { return HgfsBdGetBufInt(HGFS_LARGE_PACKET_MAX); } /* *----------------------------------------------------------------------------- * * HgfsBd_PutBuf -- * * Release a buffer obtained with HgfsBd_GetBuf. * * Results: * None * * Side effects: * None * *----------------------------------------------------------------------------- */ void HgfsBd_PutBuf(char *buf) // IN { ASSERT(buf); free(buf - HGFS_SYNC_REQREP_CLIENT_CMD_LEN); } /* *----------------------------------------------------------------------------- * * HgfsBd_GetChannel -- * * Allocate a new RpcOut channel, and try to open the connection. * * Results: * Pointer to the allocated, opened channel on success. * NULL on failure (not enough memory, or failed to open the connection). * * Side effects: * None * *----------------------------------------------------------------------------- */ RpcOut * HgfsBd_GetChannel(void) { RpcOut *out = RpcOut_Construct(); Bool status; if (!out) { Debug("HgfsBd_GetChannel: Failed to allocate an RpcOut\n"); return NULL; } status = RpcOut_start(out); if (status == FALSE) { RpcOut_Destruct(out); return NULL; } return out; } /* *----------------------------------------------------------------------------- * * HgfsBd_CloseChannel -- * * Close the channel and free the RpcOut object. * * Results: * TRUE if closing the channel succeeded, FALSE if it failed. * * Side effects: * None * *----------------------------------------------------------------------------- */ Bool HgfsBd_CloseChannel(RpcOut *out) // IN: Channel to close and free { Bool success; ASSERT(out); success = RpcOut_stop(out); if (success == TRUE) { RpcOut_Destruct(out); } return success; } /* *----------------------------------------------------------------------------- * * HgfsBd_Dispatch -- * * Get a reply to an hgfs request. We call RpcOut_Sent, which * returns a buffer with the reply in it, and we pass this back to * the caller. * * Results: * On success, returns zero. On failure, returns a negative error. * * Side effects: * None * *----------------------------------------------------------------------------- */ int HgfsBd_Dispatch(RpcOut *out, // IN: Channel to send on char *packetIn, // IN: Buf containing request packet size_t *packetSize, // IN/OUT: Size of packet in/out char const **packetOut) // OUT: Buf containing reply packet { Bool success; char const *reply; size_t replyLen; ASSERT(out); ASSERT(packetIn); ASSERT(packetSize); ASSERT(packetOut); success = RpcOut_send(out, packetIn - HGFS_CLIENT_CMD_LEN, *packetSize + HGFS_CLIENT_CMD_LEN, &reply, &replyLen); if (success == FALSE) { Debug("HgfsBd_Dispatch: RpcOut_send returned failure\n"); return -1; } ASSERT(replyLen <= HGFS_LARGE_PACKET_MAX); *packetOut = reply; *packetSize = replyLen; return 0; } /* *----------------------------------------------------------------------------- * * HgfsBd_Enabled -- * * Test to see if hgfs is enabled on the host. * * Results: * TRUE if hgfs is enabled. * FALSE if hgfs is disabled. * * Side effects: * None * *----------------------------------------------------------------------------- */ Bool HgfsBd_Enabled(RpcOut *out, // IN: RPCI Channel char *requestPacket) // IN: Buffer (obtained from HgfsBd_GetBuf) { char const *replyPacket; // Buffer returned by HgfsBd_Dispatch size_t packetSize; int error; /* * Send a bogus (empty) request to the VMX. If hgfs is disabled on * the host side then the request will fail (because the RPCI call * itself will fail). If hgfs is enabled, we will get a packet back * (it will be an error packet because our request was malformed, * but we just discard it anyway). */ packetSize = 0; error = HgfsBd_Dispatch(out, requestPacket, &packetSize, &replyPacket); if (error < 0) { return FALSE; } return TRUE; } /* *----------------------------------------------------------------------------- * * HgfsBd_OpenBackdoor -- * * Check if the HGFS channel is open, and, if not, open it. This is a * one-stop convenience wrapper around HgfsBd_Enabled, HgfsBd_GetBuf, and * HgfsBd_GetChannel. * * Results: * TRUE if the backdoor is now open, regardless of its previous state. * FALSE if the backdoor could not be opened. * * Side effects: * May open a channel to the host. * *----------------------------------------------------------------------------- */ Bool HgfsBd_OpenBackdoor(RpcOut **out) // IN/OUT: RPCI Channel { char *packetBuffer = NULL; Bool success = FALSE; ASSERT(out); /* Short-circuit: backdoor is already open. */ if (*out != NULL) { return TRUE; } /* Open the channel. */ *out = HgfsBd_GetChannel(); if (*out == NULL) { return FALSE; } /* Allocate a buffer for use in pinging the HGFS server. */ packetBuffer = HgfsBd_GetBuf(); if (packetBuffer == NULL) { goto out; } /* Ping the HGFS server. */ if (!HgfsBd_Enabled(*out, packetBuffer)) { goto out; } success = TRUE; out: if (packetBuffer != NULL) { HgfsBd_PutBuf(packetBuffer); } if (!success && *out != NULL) { HgfsBd_CloseChannel(*out); *out = NULL; } return success; } /* *----------------------------------------------------------------------------- * * HgfsBd_CloseBackdoor -- * * Closes the backdoor channel, if it's open. * * Results: * TRUE if the channel is now closed, regardless of its previous state. * FALSE if we could not close the channel. * * Side effects: * May close the channel to the host. * *----------------------------------------------------------------------------- */ Bool HgfsBd_CloseBackdoor(RpcOut **out) // IN/OUT: RPCI Channel { Bool success = TRUE; ASSERT(out); if (*out != NULL) { if (!HgfsBd_CloseChannel(*out)) { success = FALSE; } *out = NULL; } return success; } vmhgfs-only/kernelStubsLinux.c0000444000000000000000000002263512025726746015533 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ /* * kernelStubsLinux.c * * This file contains implementations of common userspace functions in terms * that the Linux kernel can understand. */ /* Must come before any kernel header file */ #include "driver-config.h" #include "kernelStubs.h" #include "compat_kernel.h" #include "compat_page.h" #include "compat_sched.h" #include #include "vm_assert.h" /* *----------------------------------------------------------------------------- * * Panic -- * * Prints the debug message and stops the system. * * Results: * None. * * Side effects: * None * *----------------------------------------------------------------------------- */ void Panic(const char *fmt, ...) // IN { va_list args; char *result; va_start(args, fmt); result = Str_Vasprintf(NULL, fmt, args); va_end(args); if (result) { printk(KERN_EMERG "%s", result); } BUG(); while (1); // Avoid compiler warning. } /* *---------------------------------------------------------------------- * * Str_Strcpy-- * * Wrapper for strcpy that checks for buffer overruns. * * Results: * Same as strcpy. * * Side effects: * None. * *---------------------------------------------------------------------- */ char * Str_Strcpy(char *buf, // OUT const char *src, // IN size_t maxSize) // IN { unsigned int *stack = (unsigned int *)&buf; size_t len; len = strlen(src); if (len >= maxSize) { Panic("%s:%d Buffer too small 0x%x\n", __FILE__,__LINE__, stack[-1]); } return memcpy(buf, src, len + 1); } /* *---------------------------------------------------------------------- * * Str_Vsnprintf -- * * Compatability wrapper b/w different libc versions * * Results: * int - number of bytes written (not including NULL terminate character), * -1 on overflow (insufficient space for NULL terminate is considered * overflow) * * NB: on overflow the buffer WILL be null terminated * * Side effects: * None * *---------------------------------------------------------------------- */ int Str_Vsnprintf(char *str, // OUT size_t size, // IN const char *format, // IN va_list arguments) // IN { int retval; retval = vsnprintf(str, size, format, arguments); /* * Linux glibc 2.0.x returns -1 and null terminates (which we shouldn't * be linking against), but glibc 2.1.x follows c99 and returns * characters that would have been written. */ if (retval >= size) { return -1; } return retval; } /* *----------------------------------------------------------------------------- * * Str_Vasprintf -- * * Allocate and format a string, using the GNU libc way to specify the * format (i.e. optionally allow the use of positional parameters) * * Results: * The allocated string on success (if 'length' is not NULL, *length * is set to the length of the allocated string) * NULL on failure * * Side effects: * None * *----------------------------------------------------------------------------- */ char * Str_Vasprintf(size_t *length, // OUT const char *format, // IN va_list arguments) // IN { /* * Simple implementation of Str_Vasprintf when userlevel libraries are not * available (e.g. for use in drivers). We just fallback to vsnprintf, * doubling if we didn't have enough space. */ unsigned int bufSize; char *buf; int retval; bufSize = strlen(format); buf = NULL; do { /* * Initial allocation of strlen(format) * 2. Should this be tunable? * XXX Yes, this could overflow and spin forever when you get near 2GB * allocations. I don't care. --rrdharan */ va_list args2; bufSize *= 2; buf = realloc(buf, bufSize); if (!buf) { return NULL; } va_copy(args2, arguments); retval = Str_Vsnprintf(buf, bufSize, format, args2); va_end(args2); } while (retval == -1); if (length) { *length = retval; } /* * Try to trim the buffer here to save memory? */ return buf; } /* *----------------------------------------------------------------------------- * * Str_Asprintf -- * * Same as Str_Vasprintf(), but parameters are passed inline --hpreg * * Results: * Same as Str_Vasprintf() * * Side effects: * Same as Str_Vasprintf() * *----------------------------------------------------------------------------- */ char * Str_Asprintf(size_t *length, // OUT const char *format, // IN ...) // IN { va_list arguments; char *result; va_start(arguments, format); result = Str_Vasprintf(length, format, arguments); va_end(arguments); return result; } /* *----------------------------------------------------------------------------- * * strdup -- * * Duplicates a string. * * Results: * A pointer to memory containing the duplicated string or NULL if no * memory was available. * * Side effects: * None * *----------------------------------------------------------------------------- */ char * strdup(const char *source) // IN { char *target = NULL; if (source) { /* * We call our special implementation of malloc() because the users of * strdup() will call free(), and that'll decrement the pointer before * freeing it. Thus, we need to make sure that the allocated block * also stores the block length before the block itself (see malloc() * below). */ unsigned int len = strlen(source); target = malloc(len + 1); if (target) { memcpy(target, source, len + 1); } } return target; } /* *---------------------------------------------------------------------------- * * malloc -- * * Allocate memory using kmalloc. There is no realloc * equivalent, so we roll our own by padding each allocation with * 4 (or 8 for 64 bit guests) extra bytes to store the block length. * * Results: * Pointer to driver heap memory, offset by 4 (or 8) * bytes from the real block pointer. * * Side effects: * None. * *---------------------------------------------------------------------------- */ void * malloc(size_t size) // IN { size_t *ptr; ptr = kmalloc(size + sizeof size, GFP_KERNEL); if (ptr) { *ptr++ = size; } return ptr; } /* *--------------------------------------------------------------------------- * * free -- * * Free memory allocated by a previous call to malloc, calloc or realloc. * * Results: * None. * * Side effects: * Calls kfree to free the real (base) pointer. * *--------------------------------------------------------------------------- */ void free(void *mem) // IN { if (mem) { size_t *dataPtr = (size_t *)mem; kfree(--dataPtr); } } /* *---------------------------------------------------------------------------- * * calloc -- * * Malloc and zero. * * Results: * Pointer to driver heap memory (see malloc, above). * * Side effects: * None. * *---------------------------------------------------------------------------- */ void * calloc(size_t num, // IN size_t len) // IN { size_t size; void *ptr; size = num * len; ptr = malloc(size); if (ptr) { memset(ptr, 0, size); } return ptr; } /* *---------------------------------------------------------------------------- * * realloc -- * * Since the driver heap has no realloc equivalent, we have to roll our * own. Fortunately, we can retrieve the block size of every block we * hand out since we stashed it at allocation time (see malloc above). * * Results: * Pointer to memory block valid for 'newSize' bytes, or NULL if * allocation failed. * * Side effects: * Could copy memory around. * *---------------------------------------------------------------------------- */ void * realloc(void* ptr, // IN size_t newSize) // IN { void *newPtr; size_t *dataPtr; size_t length, lenUsed; dataPtr = (size_t *)ptr; length = ptr ? dataPtr[-1] : 0; if (newSize == 0) { if (ptr) { free(ptr); newPtr = NULL; } else { newPtr = malloc(newSize); } } else if (newSize == length) { newPtr = ptr; } else if ((newPtr = malloc(newSize))) { if (length < newSize) { lenUsed = length; } else { lenUsed = newSize; } memcpy(newPtr, ptr, lenUsed); free(ptr); } return newPtr; } vmhgfs-only/message.c0000444000000000000000000002007612025726746013633 0ustar rootroot/********************************************************* * Copyright (C) 1999 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 * *********************************************************/ /* * message.c -- * * Second layer of the internal communication channel between guest * applications and vmware * * Build a generic messaging system between guest applications and vmware. * * The protocol is not completely symmetrical, because: * . basic requests can only be sent by guest applications (when vmware * wants to post a message to a guest application, the message will be * really fetched only when the guest application will poll for new * available messages) * . several guest applications can talk to vmware, while the contrary is * not true * * Operations that are not atomic (in terms of number of backdoor calls) * can be aborted by vmware if a checkpoint/restore occurs in the middle of * such an operation. This layer takes care of retrying those operations. */ #ifdef __cplusplus extern "C" { #endif #if defined(__KERNEL__) || defined(_KERNEL) || defined(KERNEL) # include "kernelStubs.h" #else # include # include # include "debug.h" #endif #include "backdoor_def.h" #include "guest_msg_def.h" #include "backdoor.h" #include "message.h" static MessageOpenProcType externalOpenProc = NULL; static MessageGetReadEventProcType externalGetReadEventProc = NULL; static MessageSendProcType externalSendProc = NULL; static MessageReceiveProcType externalReceiveProc = NULL; static MessageCloseProcType externalCloseProc = NULL; /* * Currently, the default implementation is to use the backdoor. Soon, * this will not be the default, as we will explicitly set it when we * decide to use the backdoor. */ EXTERN Message_Channel *MessageBackdoor_Open(uint32 proto); EXTERN Bool MessageBackdoor_GetReadEvent(Message_Channel *chan, int64 *event); EXTERN Bool MessageBackdoor_Send(Message_Channel *chan, const unsigned char *buf, size_t bufSize); EXTERN Bool MessageBackdoor_Receive(Message_Channel *chan, unsigned char **buf, size_t *bufSize); EXTERN Bool MessageBackdoor_Close(Message_Channel *chan); /* *----------------------------------------------------------------------------- * * Message_SetTransport -- * * This tells the message layer to use an alternate transport * for messages. By default, we use the backdoor, so this function * overrides that default at runtime and switches everything over to * an alternate transport. * * Result: * None * * Side-effects: * None * *----------------------------------------------------------------------------- */ void Message_SetTransport(MessageOpenProcType openProc, // IN MessageGetReadEventProcType getReadEeventProc, // IN MessageSendProcType sendProc, // IN MessageReceiveProcType receiveProc, // IN MessageCloseProcType closeProc) // IN { externalOpenProc = openProc; externalGetReadEventProc = getReadEeventProc; externalSendProc = sendProc; externalReceiveProc = receiveProc; externalCloseProc = closeProc; } /* *----------------------------------------------------------------------------- * * Message_Open -- * * Open a communication channel * * Result: * An allocated Message_Channel on success * NULL on failure * * Side-effects: * None * *----------------------------------------------------------------------------- */ Message_Channel * Message_Open(uint32 proto) // IN { /* * If there is an alterate backdoor implementation, then call that. */ if (NULL != externalOpenProc) { return((*externalOpenProc)(proto)); } /* * Otherwise, we default to the backdoor. */ return(MessageBackdoor_Open(proto)); } /* *----------------------------------------------------------------------------- * * Message_GetReadEvent -- * * This allows higher levels of the IPC stack to use an event to detect * when a message has arrived. This allows an asynchronous, event-based-model * rather than continually calling Message_Receive in a busy loop. This may * only be supported by some transports. The backdoor does not, so the IPC * code will still have to poll in those cases. * * Result: * Bool - whether this feature is supported by this transport. * * Side-effects: * None * *----------------------------------------------------------------------------- */ Bool Message_GetReadEvent(Message_Channel *chan, // IN int64 *event) // OUT { /* * If there is an alterate backdoor implementation, then call that. */ if (NULL != externalGetReadEventProc) { return((*externalGetReadEventProc)(chan, event)); } /* * Otherwise, we default to the backdoor. */ return(MessageBackdoor_GetReadEvent(chan, event)); } /* *----------------------------------------------------------------------------- * * Message_Send -- * * Send a message over a communication channel * * Result: * TRUE on success * FALSE on failure (the message is discarded by vmware) * * Side-effects: * None * *----------------------------------------------------------------------------- */ Bool Message_Send(Message_Channel *chan, // IN/OUT const unsigned char *buf, // IN size_t bufSize) // IN { /* * If there is an alterate backdoor implementation, then call that. */ if (NULL != externalSendProc) { return((*externalSendProc)(chan, buf, bufSize)); } /* * Otherwise, we default to the backdoor. */ return(MessageBackdoor_Send(chan, buf, bufSize)); } /* *----------------------------------------------------------------------------- * * Message_Receive -- * * If vmware has posted a message for this channel, retrieve it * * Result: * TRUE on success (bufSize is 0 if there is no message) * FALSE on failure * * Side-effects: * None * *----------------------------------------------------------------------------- */ Bool Message_Receive(Message_Channel *chan, // IN/OUT unsigned char **buf, // OUT size_t *bufSize) // OUT { /* * If there is an alterate backdoor implementation, then call that. */ if (NULL != externalReceiveProc) { return((*externalReceiveProc)(chan, buf, bufSize)); } /* * Otherwise, we default to the backdoor. */ return(MessageBackdoor_Receive(chan, buf, bufSize)); } /* *----------------------------------------------------------------------------- * * Message_Close -- * * Close a communication channel * * Result: * TRUE on success, the channel is destroyed * FALSE on failure * * Side-effects: * None * *----------------------------------------------------------------------------- */ Bool Message_Close(Message_Channel *chan) // IN/OUT { /* * If there is an alterate backdoor implementation, then call that. */ if (NULL != externalCloseProc) { return((*externalCloseProc)(chan)); } /* * Otherwise, we default to the backdoor. */ return(MessageBackdoor_Close(chan)); } #ifdef __cplusplus } #endif vmhgfs-only/messageBackdoor.c0000444000000000000000000004156112025726746015302 0ustar rootroot/********************************************************* * Copyright (C) 1999 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 * *********************************************************/ /* * messageBackdoor.c -- * * Second layer of the internal communication channel between guest * applications and vmware * * Build a generic messaging system between guest applications and vmware. * * The protocol is not completely symmetrical, because: * . basic requests can only be sent by guest applications (when vmware * wants to post a message to a guest application, the message will be * really fetched only when the guest application will poll for new * available messages) * . several guest applications can talk to vmware, while the contrary is * not true * * Operations that are not atomic (in terms of number of backdoor calls) * can be aborted by vmware if a checkpoint/restore occurs in the middle of * such an operation. This layer takes care of retrying those operations. */ #ifdef __cplusplus extern "C" { #endif #if defined(__KERNEL__) || defined(_KERNEL) || defined(KERNEL) # include "kernelStubs.h" #else # include # include # include "debug.h" #endif #ifdef MESSAGE_DEBUG # define MESSAGE_LOG(args) Debug args #else /* MESSAGE_DEBUG */ # define MESSAGE_LOG(args) #endif /* MESSAGE_DEBUG */ #include "backdoor_def.h" #include "guest_msg_def.h" #include "backdoor.h" #include "message.h" /* The channel object */ struct Message_Channel { /* Identifier */ uint16 id; /* Reception buffer */ /* Data */ unsigned char *in; /* Allocated size */ size_t inAlloc; /* The cookie */ uint32 cookieHigh; uint32 cookieLow; }; /* *----------------------------------------------------------------------------- * * MessageBackdoor_Open -- * * Open a communication channel * * Result: * An allocated Message_Channel on success * NULL on failure * * Side-effects: * None * *----------------------------------------------------------------------------- */ Message_Channel * MessageBackdoor_Open(uint32 proto) // IN { Message_Channel *chan; uint32 flags; Backdoor_proto bp; chan = (Message_Channel *)malloc(sizeof(*chan)); if (chan == NULL) { MESSAGE_LOG(("Message: Not enough memory\n")); goto error_quit; } flags = GUESTMSG_FLAG_COOKIE; retry: /* IN: Type */ bp.in.cx.halfs.high = MESSAGE_TYPE_OPEN; /* IN: Magic number of the protocol and flags */ bp.in.size = proto | flags; bp.in.cx.halfs.low = BDOOR_CMD_MESSAGE; Backdoor(&bp); /* OUT: Status */ if ((bp.in.cx.halfs.high & MESSAGE_STATUS_SUCCESS) == 0) { if (flags) { /* Cookies not supported. Fall back to no cookie. --hpreg */ flags = 0; goto retry; } MESSAGE_LOG(("Message: Unable to open a communication channel\n")); goto error_quit; } /* OUT: Id and cookie */ chan->id = bp.in.dx.halfs.high; chan->cookieHigh = bp.out.si.word; chan->cookieLow = bp.out.di.word; /* Initialize the channel */ chan->in = NULL; chan->inAlloc = 0; MESSAGE_LOG(("Message: Communication channel %u opened\n", chan->id)); return chan; error_quit: free(chan); chan = NULL; return NULL; } /* *----------------------------------------------------------------------------- * * MessageBackdoor_GetReadEvent -- * * This allows higher levels of the IPC stack to use an event to detect * when a message has arrived. This allows an interrupt-model rather than * continually calling Message_Receive in a busy loop. This may only be supported * by some transports. The backdoor does not, so the IPC code will still * have to poll in those cases. * * Result: * Bool - whether this feature is supported by this transport. * * Side-effects: * None * *----------------------------------------------------------------------------- */ Bool MessageBackdoor_GetReadEvent(Message_Channel *chan, // IN int64 *event) // OUT { return FALSE; } /* *----------------------------------------------------------------------------- * * MessageBackdoor_Send -- * * Send a message over a communication channel * * Result: * TRUE on success * FALSE on failure (the message is discarded by vmware) * * Side-effects: * None * *----------------------------------------------------------------------------- */ Bool MessageBackdoor_Send(Message_Channel *chan, // IN/OUT const unsigned char *buf, // IN size_t bufSize) // IN { const unsigned char *myBuf; size_t myBufSize; Backdoor_proto bp; retry: myBuf = buf; myBufSize = bufSize; /* * Send the size. */ /* IN: Type */ bp.in.cx.halfs.high = MESSAGE_TYPE_SENDSIZE; /* IN: Id and cookie */ bp.in.dx.halfs.high = chan->id; bp.in.si.word = chan->cookieHigh; bp.in.di.word = chan->cookieLow; /* IN: Size */ bp.in.size = myBufSize; bp.in.cx.halfs.low = BDOOR_CMD_MESSAGE; Backdoor(&bp); /* OUT: Status */ if ((bp.in.cx.halfs.high & MESSAGE_STATUS_SUCCESS) == 0) { MESSAGE_LOG(("Message: Unable to send a message over the communication " "channel %u\n", chan->id)); return FALSE; } if (bp.in.cx.halfs.high & MESSAGE_STATUS_HB) { /* * High-bandwidth backdoor port supported. Send the message in one * backdoor operation. --hpreg */ if (myBufSize) { Backdoor_proto_hb bphb; bphb.in.bx.halfs.low = BDOORHB_CMD_MESSAGE; bphb.in.bx.halfs.high = MESSAGE_STATUS_SUCCESS; bphb.in.dx.halfs.high = chan->id; bphb.in.bp.word = chan->cookieHigh; bphb.in.dstAddr = chan->cookieLow; bphb.in.size = myBufSize; bphb.in.srcAddr = (uintptr_t) myBuf; Backdoor_HbOut(&bphb); if ((bphb.in.bx.halfs.high & MESSAGE_STATUS_SUCCESS) == 0) { if ((bphb.in.bx.halfs.high & MESSAGE_STATUS_CPT) != 0) { /* A checkpoint occurred. Retry the operation. --hpreg */ goto retry; } MESSAGE_LOG(("Message: Unable to send a message over the " "communication channel %u\n", chan->id)); return FALSE; } } } else { /* * High-bandwidth backdoor port not supported. Send the message, 4 bytes * at a time. --hpreg */ for (;;) { if (myBufSize == 0) { /* We are done */ break; } /* IN: Type */ bp.in.cx.halfs.high = MESSAGE_TYPE_SENDPAYLOAD; /* IN: Id and cookie */ bp.in.dx.halfs.high = chan->id; bp.in.si.word = chan->cookieHigh; bp.in.di.word = chan->cookieLow; /* IN: Piece of message */ /* * Beware in case we are not allowed to read extra bytes beyond the * end of the buffer. */ switch (myBufSize) { case 1: bp.in.size = myBuf[0]; myBufSize -= 1; break; case 2: bp.in.size = myBuf[0] | myBuf[1] << 8; myBufSize -= 2; break; case 3: bp.in.size = myBuf[0] | myBuf[1] << 8 | myBuf[2] << 16; myBufSize -= 3; break; default: bp.in.size = *(const uint32 *)myBuf; myBufSize -= 4; break; } bp.in.cx.halfs.low = BDOOR_CMD_MESSAGE; Backdoor(&bp); /* OUT: Status */ if ((bp.in.cx.halfs.high & MESSAGE_STATUS_SUCCESS) == 0) { if ((bp.in.cx.halfs.high & MESSAGE_STATUS_CPT) != 0) { /* A checkpoint occurred. Retry the operation. --hpreg */ goto retry; } MESSAGE_LOG(("Message: Unable to send a message over the " "communication channel %u\n", chan->id)); return FALSE; } myBuf += 4; } } MESSAGE_LOG(("Message: Sent a message over the communication channel %u\n", chan->id)); return TRUE; } /* *----------------------------------------------------------------------------- * * MessageBackdoor_Receive -- * * If vmware has posted a message for this channel, retrieve it * * Result: * TRUE on success (bufSize is 0 if there is no message) * FALSE on failure * * Side-effects: * None * *----------------------------------------------------------------------------- */ Bool MessageBackdoor_Receive(Message_Channel *chan, // IN/OUT unsigned char **buf, // OUT size_t *bufSize) // OUT { Backdoor_proto bp; size_t myBufSize; unsigned char *myBuf; retry: /* * Is there a message waiting for our retrieval? */ /* IN: Type */ bp.in.cx.halfs.high = MESSAGE_TYPE_RECVSIZE; /* IN: Id and cookie */ bp.in.dx.halfs.high = chan->id; bp.in.si.word = chan->cookieHigh; bp.in.di.word = chan->cookieLow; bp.in.cx.halfs.low = BDOOR_CMD_MESSAGE; Backdoor(&bp); /* OUT: Status */ if ((bp.in.cx.halfs.high & MESSAGE_STATUS_SUCCESS) == 0) { MESSAGE_LOG(("Message: Unable to poll for messages over the " "communication channel %u\n", chan->id)); return FALSE; } if ((bp.in.cx.halfs.high & MESSAGE_STATUS_DORECV) == 0) { /* No message to retrieve */ *bufSize = 0; return TRUE; } /* * Receive the size. */ /* OUT: Type */ if (bp.in.dx.halfs.high != MESSAGE_TYPE_SENDSIZE) { MESSAGE_LOG(("Message: Protocol error. Expected a " "MESSAGE_TYPE_SENDSIZE request from vmware\n")); return FALSE; } /* OUT: Size */ myBufSize = bp.out.bx.word; /* * Allocate an extra byte for a trailing NUL character. The code that will * deal with this message may not know about binary strings, and may expect * a C string instead. --hpreg */ if (myBufSize + 1 > chan->inAlloc) { myBuf = (unsigned char *)realloc(chan->in, myBufSize + 1); if (myBuf == NULL) { MESSAGE_LOG(("Message: Not enough memory to receive a message over " "the communication channel %u\n", chan->id)); goto error_quit; } chan->in = myBuf; chan->inAlloc = myBufSize + 1; } *bufSize = myBufSize; myBuf = *buf = chan->in; if (bp.in.cx.halfs.high & MESSAGE_STATUS_HB) { /* * High-bandwidth backdoor port supported. Receive the message in one * backdoor operation. --hpreg */ if (myBufSize) { Backdoor_proto_hb bphb; bphb.in.bx.halfs.low = BDOORHB_CMD_MESSAGE; bphb.in.bx.halfs.high = MESSAGE_STATUS_SUCCESS; bphb.in.dx.halfs.high = chan->id; bphb.in.srcAddr = chan->cookieHigh; bphb.in.bp.word = chan->cookieLow; bphb.in.size = myBufSize; bphb.in.dstAddr = (uintptr_t) myBuf; Backdoor_HbIn(&bphb); if ((bphb.in.bx.halfs.high & MESSAGE_STATUS_SUCCESS) == 0) { if ((bphb.in.bx.halfs.high & MESSAGE_STATUS_CPT) != 0) { /* A checkpoint occurred. Retry the operation. --hpreg */ goto retry; } MESSAGE_LOG(("Message: Unable to receive a message over the " "communication channel %u\n", chan->id)); goto error_quit; } } } else { /* * High-bandwidth backdoor port not supported. Receive the message, 4 * bytes at a time. --hpreg */ for (;;) { if (myBufSize == 0) { /* We are done */ break; } /* IN: Type */ bp.in.cx.halfs.high = MESSAGE_TYPE_RECVPAYLOAD; /* IN: Id and cookie */ bp.in.dx.halfs.high = chan->id; bp.in.si.word = chan->cookieHigh; bp.in.di.word = chan->cookieLow; /* IN: Status for the previous request (that succeeded) */ bp.in.size = MESSAGE_STATUS_SUCCESS; bp.in.cx.halfs.low = BDOOR_CMD_MESSAGE; Backdoor(&bp); /* OUT: Status */ if ((bp.in.cx.halfs.high & MESSAGE_STATUS_SUCCESS) == 0) { if ((bp.in.cx.halfs.high & MESSAGE_STATUS_CPT) != 0) { /* A checkpoint occurred. Retry the operation. --hpreg */ goto retry; } MESSAGE_LOG(("Message: Unable to receive a message over the " "communication channel %u\n", chan->id)); goto error_quit; } /* OUT: Type */ if (bp.in.dx.halfs.high != MESSAGE_TYPE_SENDPAYLOAD) { MESSAGE_LOG(("Message: Protocol error. Expected a " "MESSAGE_TYPE_SENDPAYLOAD from vmware\n")); goto error_quit; } /* OUT: Piece of message */ /* * Beware in case we are not allowed to write extra bytes beyond the * end of the buffer. --hpreg */ switch (myBufSize) { case 1: myBuf[0] = bp.out.bx.word & 0xff; myBufSize -= 1; break; case 2: myBuf[0] = bp.out.bx.word & 0xff; myBuf[1] = (bp.out.bx.word >> 8) & 0xff; myBufSize -= 2; break; case 3: myBuf[0] = bp.out.bx.word & 0xff; myBuf[1] = (bp.out.bx.word >> 8) & 0xff; myBuf[2] = (bp.out.bx.word >> 16) & 0xff; myBufSize -= 3; break; default: *(uint32 *)myBuf = bp.out.bx.word; myBufSize -= 4; break; } myBuf += 4; } } /* Write a trailing NUL just after the message. --hpreg */ chan->in[*bufSize] = '\0'; /* IN: Type */ bp.in.cx.halfs.high = MESSAGE_TYPE_RECVSTATUS; /* IN: Id and cookie */ bp.in.dx.halfs.high = chan->id; bp.in.si.word = chan->cookieHigh; bp.in.di.word = chan->cookieLow; /* IN: Status for the previous request (that succeeded) */ bp.in.size = MESSAGE_STATUS_SUCCESS; bp.in.cx.halfs.low = BDOOR_CMD_MESSAGE; Backdoor(&bp); /* OUT: Status */ if ((bp.in.cx.halfs.high & MESSAGE_STATUS_SUCCESS) == 0) { if ((bp.in.cx.halfs.high & MESSAGE_STATUS_CPT) != 0) { /* A checkpoint occurred. Retry the operation. --hpreg */ goto retry; } MESSAGE_LOG(("Message: Unable to receive a message over the " "communication channel %u\n", chan->id)); goto error_quit; } return TRUE; error_quit: /* IN: Type */ if (myBufSize == 0) { bp.in.cx.halfs.high = MESSAGE_TYPE_RECVSTATUS; } else { bp.in.cx.halfs.high = MESSAGE_TYPE_RECVPAYLOAD; } /* IN: Id and cookie */ bp.in.dx.halfs.high = chan->id; bp.in.si.word = chan->cookieHigh; bp.in.di.word = chan->cookieLow; /* IN: Status for the previous request (that failed) */ bp.in.size = 0; bp.in.cx.halfs.low = BDOOR_CMD_MESSAGE; Backdoor(&bp); /* OUT: Status */ if ((bp.in.cx.halfs.high & MESSAGE_STATUS_SUCCESS) == 0) { MESSAGE_LOG(("Message: Unable to signal an error of reception over the " "communication channel %u\n", chan->id)); return FALSE; } return FALSE; } /* *----------------------------------------------------------------------------- * * MessageBackdoor_Close -- * * Close a communication channel * * Result: * TRUE on success, the channel is destroyed * FALSE on failure * * Side-effects: * None * *----------------------------------------------------------------------------- */ Bool MessageBackdoor_Close(Message_Channel *chan) // IN/OUT { Backdoor_proto bp; Bool ret = TRUE; /* IN: Type */ bp.in.cx.halfs.high = MESSAGE_TYPE_CLOSE; /* IN: Id and cookie */ bp.in.dx.halfs.high = chan->id; bp.in.si.word = chan->cookieHigh; bp.in.di.word = chan->cookieLow; bp.in.cx.halfs.low = BDOOR_CMD_MESSAGE; Backdoor(&bp); /* OUT: Status */ if ((bp.in.cx.halfs.high & MESSAGE_STATUS_SUCCESS) == 0) { MESSAGE_LOG(("Message: Unable to close the communication channel %u\n", chan->id)); ret = FALSE; } else { MESSAGE_LOG(("Message: Communication channel %u closed\n", chan->id)); } free(chan->in); chan->in = NULL; free(chan); return ret; } #ifdef __cplusplus } #endif vmhgfs-only/rpcout.c0000444000000000000000000002730712025726746013527 0ustar rootroot/********************************************************* * Copyright (C) 2004 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 * *********************************************************/ /* * rpcout.c -- * * Remote Procedure Call between VMware and guest applications * C implementation. * * This module contains implements the out (guest=>host) direction only. * The in and out modules are separate since some applications (e.g. * drivers that want to do RPC-based logging) only want/need/can have the * out direction. */ #if defined(__KERNEL__) || defined(_KERNEL) || defined(KERNEL) # include "kernelStubs.h" #else # include # include # include # include # include "str.h" # include "debug.h" #endif #include "vmware.h" #include "rpcout.h" #include "message.h" /* * The RpcOut object */ struct RpcOut { Message_Channel *channel; }; /* *----------------------------------------------------------------------------- * * RpcOut_Construct -- * * Constructor for the RpcOut object * * Results: * New RpcOut object. * * Side effects: * Allocates memory. * *----------------------------------------------------------------------------- */ RpcOut * RpcOut_Construct(void) { return (RpcOut *)calloc(1, sizeof(RpcOut)); } /* *----------------------------------------------------------------------------- * * RpcOut_Destruct -- * * Destructor for the RpcOut object. * * Results: * None. * * Side effects: * Frees RpcOut object memory. * *----------------------------------------------------------------------------- */ void RpcOut_Destruct(RpcOut *out) // IN { ASSERT(out); ASSERT(out->channel == NULL); free(out); } /* *----------------------------------------------------------------------------- * * RpcOut_start -- * * Open the channel * * Result: * TRUE on success * FALSE on failure * * Side-effects: * None * *----------------------------------------------------------------------------- */ Bool RpcOut_start(RpcOut *out) // IN { ASSERT(out); ASSERT(out->channel == NULL); out->channel = Message_Open(RPCI_PROTOCOL_NUM); if (out->channel == NULL) { Debug("RpcOut: couldn't open channel with RPCI protocol\n"); return FALSE; } return TRUE; } /* *----------------------------------------------------------------------------- * * RpcOut_send -- * * Make VMware synchroneously execute a TCLO command * * Unlike the other send varieties, RpcOut_send requires that the * caller pass non-NULL reply and repLen arguments. * * Result * TRUE on success. 'reply' contains the result of the rpc * FALSE on error. 'reply' will contain a description of the error * * In both cases, the caller should not free the reply. * * Side-effects * None * *----------------------------------------------------------------------------- */ Bool RpcOut_send(RpcOut *out, // IN char const *request, // IN size_t reqLen, // IN char const **reply, // OUT size_t *repLen) // OUT { unsigned char *myReply; size_t myRepLen; Bool success; ASSERT(out); ASSERT(out->channel); if (Message_Send(out->channel, (const unsigned char *)request, reqLen) == FALSE) { *reply = "RpcOut: Unable to send the RPCI command"; *repLen = strlen(*reply); return FALSE; } if (Message_Receive(out->channel, &myReply, &myRepLen) == FALSE) { *reply = "RpcOut: Unable to receive the result of the RPCI command"; *repLen = strlen(*reply); return FALSE; } if (myRepLen < 2 || ( (success = strncmp((const char *)myReply, "1 ", 2) == 0) == FALSE && strncmp((const char *)myReply, "0 ", 2))) { *reply = "RpcOut: Invalid format for the result of the RPCI command"; *repLen = strlen(*reply); return FALSE; } *reply = ((const char *)myReply) + 2; *repLen = myRepLen - 2; return success; } /* *----------------------------------------------------------------------------- * * RpcOut_stop -- * * Close the channel * * Result * TRUE on success * FALSE on failure * * Side-effects * Frees the result of the last command. * *----------------------------------------------------------------------------- */ Bool RpcOut_stop(RpcOut *out) // IN { Bool status; ASSERT(out); status = TRUE; if (out->channel) { /* Try to close the channel */ if (Message_Close(out->channel) == FALSE) { Debug("RpcOut: couldn't close channel\n"); status = FALSE; } out->channel = NULL; } return status; } /* *----------------------------------------------------------------------------- * * RpcOut_sendOne -- * * Make VMware execute a RPCI command * * VMware closes a channel when it detects that there has been no activity * on it for a while. Because we do not know how often this program will * make VMware execute a RPCI, we open/close one channel per RPCI command * * Return value: * TRUE on success. '*reply' contains an allocated result of the rpc * FALSE on error. '*reply' contains an allocated description of the error * or NULL. * * Side effects: * None * *----------------------------------------------------------------------------- */ Bool RpcOut_sendOne(char **reply, // OUT: Result size_t *repLen, // OUT: Length of the result char const *reqFmt, // IN: RPCI command ...) // Unspecified { va_list args; Bool status; char *request; size_t reqLen = 0; status = FALSE; /* Format the request string */ va_start(args, reqFmt); request = Str_Vasprintf(&reqLen, reqFmt, args); va_end(args); /* * If Str_Vasprintf failed, write NULL into the reply if the caller wanted * a reply back. */ if (request == NULL) { if (reply) { *reply = NULL; } return FALSE; } /* * If the command doesn't contain a space, add one to the end to maintain * compatibility with old VMXs. * * For a long time, the GuestRpc logic in the VMX was wired to expect a * trailing space in every command, even commands without arguments. That is * no longer true, but we must continue to add a trailing space because we * don't know whether we're talking to an old or new VMX. */ if (strchr(request, ' ') == NULL) { char *tmp; tmp = Str_Asprintf(NULL, "%s ", request); free(request); request = tmp; /* * If Str_Asprintf failed, write NULL into reply if the caller wanted * a reply back. */ if (request == NULL) { if (reply != NULL) { *reply = NULL; } return FALSE; } } status = RpcOut_SendOneRaw(request, reqLen, reply, repLen); free(request); return status; } /* *----------------------------------------------------------------------------- * * RpcOut_SendOneRaw -- * * Make VMware execute a RPCI command * * VMware closes a channel when it detects that there has been no activity * on it for a while. Because we do not know how often this program will * make VMware execute a RPCI, we open/close one channel per RPCI command. * * This function sends a message over the backdoor without using * any of the Str_ functions on the request buffer; Str_Asprintf() in * particular uses FormatMessage on Win32, which corrupts some UTF-8 * strings. Using this function directly instead of using RpcOut_SendOne() * avoids these problems. * * If this is not an issue, you can use RpcOut_sendOne(), which has * varargs. * * Note: It is the caller's responsibility to ensure that the RPCI command * followed by a space appear at the start of the request buffer. See * the command in RpcOut_sendOne for details. * * Return value: * TRUE on success. '*reply' contains an allocated result of the rpc * FALSE on error. '*reply' contains an allocated description of the * error or NULL. * * * Side effects: * None * *----------------------------------------------------------------------------- */ Bool RpcOut_SendOneRaw(void *request, // IN: RPCI command size_t reqLen, // IN: Size of request buffer char **reply, // OUT: Result size_t *repLen) // OUT: Length of the result { Bool status; RpcOut *out = NULL; char const *myReply; size_t myRepLen; status = FALSE; Debug("Rpci: Sending request='%s'\n", (char *)request); out = RpcOut_Construct(); if (out == NULL) { myReply = "RpcOut: Unable to create the RpcOut object"; myRepLen = strlen(myReply); goto sent; } else if (RpcOut_start(out) == FALSE) { myReply = "RpcOut: Unable to open the communication channel"; myRepLen = strlen(myReply); goto sent; } else if (RpcOut_send(out, request, reqLen, &myReply, &myRepLen) == FALSE) { /* We already have the description of the error */ goto sent; } status = TRUE; sent: Debug("Rpci: Sent request='%s', reply='%s', len=%"FMTSZ"u, status=%d\n", (char *)request, myReply, myRepLen, status); if (reply != NULL) { /* * If we got a non-NULL reply, make a copy of it, because the reply * we got back is inside the channel buffer, which will get destroyed * at the end of this function. */ if (myReply != NULL) { /* * We previously used strdup to duplicate myReply, but that * breaks if you are sending binary (not string) data over the * backdoor. Don't assume the data is a string. * * myRepLen is strlen(myReply), so we need an extra byte to * cover the NUL terminator. */ *reply = malloc(myRepLen + 1); if (*reply != NULL) { memcpy(*reply, myReply, myRepLen); /* * The message layer already writes a trailing NUL but we might * change that someday, so do it again here. */ (*reply)[myRepLen] = 0; } } else { /* * Our reply was NULL, so just pass the NULL back up to the caller. */ *reply = NULL; } /* * Only set the length if the caller wanted it and if we got a good * reply. */ if (repLen != NULL && *reply != NULL) { *repLen = myRepLen; } } if (out) { if (RpcOut_stop(out) == FALSE) { /* * We couldn't stop the channel. Free anything we allocated, give our * client a reply of NULL, and return FALSE. */ if (reply != NULL) { free(*reply); *reply = NULL; } Debug("Rpci: unable to close the communication channel\n"); status = FALSE; } RpcOut_Destruct(out); out = NULL; } return status; } vmhgfs-only/backdoor.h0000444000000000000000000000247112025726746013777 0ustar rootroot/********************************************************* * Copyright (C) 1999 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 * *********************************************************/ /* * backdoor.h -- * * First layer of the internal communication channel between guest * applications and vmware */ #ifndef _BACKDOOR_H_ #define _BACKDOOR_H_ #include "vm_basic_types.h" #include "vm_assert.h" #include "backdoor_types.h" void Backdoor(Backdoor_proto *bp); // IN/OUT void Backdoor_InOut(Backdoor_proto *bp); // IN/OUT void Backdoor_HbOut(Backdoor_proto_hb *bp); // IN/OUT void Backdoor_HbIn(Backdoor_proto_hb *bp); // IN/OUT #endif /* _BACKDOOR_H_ */ vmhgfs-only/hgfsBd.h0000444000000000000000000000271312025726746013407 0ustar rootroot/********************************************************* * Copyright (C) 1998 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 * *********************************************************/ #ifndef _HGFS_BD_H_ # define _HGFS_BD_H_ /* * hgfsBd.h -- * * Backdoor calls used by hgfs clients. */ #include "rpcout.h" char *HgfsBd_GetBuf(void); char *HgfsBd_GetLargeBuf(void); void HgfsBd_PutBuf(char *); RpcOut *HgfsBd_GetChannel(void); Bool HgfsBd_CloseChannel(RpcOut *out); int HgfsBd_Dispatch(RpcOut *out, char *packetIn, size_t *packetSize, char const **packetOut); Bool HgfsBd_Enabled(RpcOut *out, char *requestPacket); Bool HgfsBd_OpenBackdoor(RpcOut **out); Bool HgfsBd_CloseBackdoor(RpcOut **out); #endif // _HGFS_BD_H_ vmhgfs-only/kernelStubs.h0000444000000000000000000001124612025726746014514 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ /* * kernelStubs.h * * KernelStubs implements some userspace library functions in terms * of kernel functions to allow library userspace code to be used in a * kernel. */ #ifndef __KERNELSTUBS_H__ #define __KERNELSTUBS_H__ #ifdef linux # ifndef __KERNEL__ # error "__KERNEL__ is not defined" # endif # include "driver-config.h" // Must be included before any other header files # include "vm_basic_types.h" # include # include #elif defined(_WIN32) # include "vm_basic_types.h" # include /* kernel memory APIs */ # include /* for _vsnprintf, vsprintf */ # include /* for va_start stuff */ # include /* for min macro. */ # include "vm_assert.h" /* Our assert macros */ #elif defined(__FreeBSD__) # include "vm_basic_types.h" # ifndef _KERNEL # error "_KERNEL is not defined" # endif # include # include # include # include # include # include # include "vm_assert.h" #elif defined(__APPLE__) # include "vm_basic_types.h" # ifndef KERNEL # error "KERNEL is not defined" # endif # include # include #endif /* * Function Prototypes */ #if defined(linux) || defined(__APPLE__) /* if (linux) || (__APPLE__) { */ # ifdef linux /* if (linux) { */ char *strdup(const char *source); # endif /* Shared between Linux and Apple kernel stubs. */ void *malloc(size_t size); void free(void *mem); void *calloc(size_t num, size_t len); void *realloc(void *ptr, size_t newSize); #elif defined(_WIN32) /* } else if (_WIN32) { */ #if (_WIN32_WINNT == 0x0400) /* The following declarations are missing on NT4. */ typedef unsigned int UINT_PTR; typedef unsigned int SIZE_T; /* No free with tag availaible on NT4 kernel! */ #define KRNL_STUBS_FREE(P,T) ExFreePool((P)) #else /* _WIN32_WINNT */ #define KRNL_STUBS_FREE(P,T) ExFreePoolWithTag((P),(T)) /* Win 2K and later useful kernel function, documented but not declared! */ NTKERNELAPI VOID ExFreePoolWithTag(IN PVOID P, IN ULONG Tag); #endif /* _WIN32_WINNT */ #elif defined(__FreeBSD__) /* } else if (FreeBSD) { */ /* Kernel memory on FreeBSD is tagged for statistics and sanity checking. */ MALLOC_DECLARE(M_VMWARE_TEMP); /* * On FreeBSD, the general memory allocator for both userland and the kernel is named * malloc, but the kernel malloc() takes more arguments. The following alias & macros * work around this, to provide the standard malloc() API for userspace code that is * being used in the kernel. */ # undef malloc static INLINE void * __compat_malloc(unsigned long size, struct malloc_type *type, int flags) { return malloc(size, type, flags); } # define malloc(size) __compat_malloc(size, M_VMWARE_TEMP, M_NOWAIT) # define calloc(count, size) __compat_malloc((count) * (size), \ M_VMWARE_TEMP, M_NOWAIT|M_ZERO) # define realloc(buf, size) realloc(buf, size, M_VMWARE_TEMP, M_NOWAIT) # define free(buf) free(buf, M_VMWARE_TEMP) # define strchr(s,c) index(s,c) # define strrchr(s,c) rindex(s,c) #endif /* } */ /* * Stub functions we provide. */ void Panic(const char *fmt, ...); char *Str_Strcpy(char *buf, const char *src, size_t maxSize); int Str_Vsnprintf(char *str, size_t size, const char *format, va_list arguments); char *Str_Vasprintf(size_t *length, const char *format, va_list arguments); char *Str_Asprintf(size_t *length, const char *Format, ...); /* * Functions the driver must implement for the stubs. */ EXTERN void Debug(const char *fmt, ...); #endif /* __KERNELSTUBS_H__ */ vmhgfs-only/message.h0000444000000000000000000000671112025726746013640 0ustar rootroot/********************************************************* * Copyright (C) 1999 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 * *********************************************************/ /* * message.h -- * * Second layer of the internal communication channel between guest * applications and vmware */ #ifndef __MESSAGE_H__ # define __MESSAGE_H__ #ifdef __cplusplus extern "C" { #endif #include "vm_basic_types.h" typedef struct Message_Channel Message_Channel; /* * These functions must be implemented by any external Message * transport implementation. Some examples include crossTalk, * a network socket, or a Microsoft Hypervisor backdoor. * * These external functions mirror the same corresponding Message_* * functions below. */ typedef Message_Channel *(*MessageOpenProcType)(uint32 proto); typedef Bool (*MessageGetReadEventProcType)(Message_Channel *chan, int64 *readEvent); typedef Bool (*MessageSendProcType)(Message_Channel *chan, const unsigned char *buf, size_t bufSize); typedef Bool (*MessageReceiveProcType)(Message_Channel *chan, unsigned char **buf, size_t *bufSize); typedef Bool (*MessageCloseProcType)(Message_Channel *chan); /* * This tells the message layer to use an alternate transport * for messages. By default, we use the backdoor, so this function * overrides that default at runtime and switches everything over to * an alternate transport. */ void Message_SetTransport(MessageOpenProcType openProc, MessageGetReadEventProcType getReadEeventProc, MessageSendProcType sendProc, MessageReceiveProcType receiveProc, MessageCloseProcType closeProc); void MessageStub_RegisterTransport(void); Message_Channel * Message_Open(uint32 proto); // IN /* * This allows higher levels of the IPC stack to use an event to detect * when a message has arrived. This allows an interrupt-model rather than * continually calling Message_Receive in a busy loop. This may only be supported * by some transports. The backdoor does not, so the IPC code will still * have to poll in those cases. */ Bool Message_GetReadEvent(Message_Channel *chan, // IN int64 *event); // OUT Bool Message_Send(Message_Channel *chan, // IN/OUT const unsigned char *buf, // IN size_t bufSize); // IN Bool Message_Receive(Message_Channel *chan, // IN/OUT unsigned char **buf, // OUT size_t *bufSize); // OUT Bool Message_Close(Message_Channel *chan); // IN/OUT #ifdef __cplusplus } #endif #endif /* __MESSAGE_H__ */ vmhgfs-only/rpcout.h0000444000000000000000000000365312025726746013532 0ustar rootroot/********************************************************* * Copyright (C) 2007 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 * *********************************************************/ /* * rpcout.h -- * * Remote Procedure Call between VMware and guest applications * C declarations */ #ifndef __RPCOUT_H__ # define __RPCOUT_H__ #include "vm_basic_types.h" #define RPCI_PROTOCOL_NUM 0x49435052 /* 'RPCI' ;-) */ typedef struct RpcOut RpcOut; RpcOut *RpcOut_Construct(void); void RpcOut_Destruct(RpcOut *out); Bool RpcOut_start(RpcOut *out); Bool RpcOut_send(RpcOut *out, char const *request, size_t reqLen, char const **reply, size_t *repLen); Bool RpcOut_stop(RpcOut *out); /* * This is the only method needed to send a message to vmware for * 99% of uses. I'm leaving the others defined here so people know * they can be exported again if the need arises. [greg] */ Bool RpcOut_sendOne(char **reply, size_t *repLen, char const *reqFmt, ...); /* * A new version of the above function that works with UTF-8 strings * and other data that would be corrupted by Win32's FormatMessage * function (which is used by RpcOut_sendOne()). */ Bool RpcOut_SendOneRaw(void *request, size_t reqLen, char **reply, size_t *repLen); #endif /* __RPCOUT_H__ */ vmhgfs-only/compat_completion.h0000444000000000000000000001371012025726746015725 0ustar rootroot/********************************************************* * Copyright (C) 2004 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 * *********************************************************/ #ifndef __COMPAT_COMPLETION_H__ # define __COMPAT_COMPLETION_H__ /* * The kernel's completion objects were made available for module use in 2.4.9. * * Between 2.4.0 and 2.4.9, we implement completions on our own using * waitqueues and counters. This was done so that we could safely support * functions like complete_all(), which cannot be implemented using semaphores. * * Prior to that, the waitqueue API is substantially different, and since none * of our modules that are built against older kernels need complete_all(), * we fallback on a simple semaphore-based implementation. */ /* * Native completions. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 9) #include #define compat_completion struct completion #define compat_init_completion(comp) init_completion(comp) #define COMPAT_DECLARE_COMPLETION DECLARE_COMPLETION #define compat_wait_for_completion(comp) wait_for_completion(comp) #define compat_complete(comp) complete(comp) /* complete_all() was exported in 2.6.6. */ # if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 6) # include "compat_wait.h" # include "compat_list.h" # include "compat_spinlock.h" # include "compat_sched.h" # define compat_complete_all(x) \ ({ \ struct list_head *currLinks; \ spin_lock(&(x)->wait.lock); \ (x)->done += UINT_MAX/2; \ \ list_for_each(currLinks, &(x)->wait.task_list) { \ wait_queue_t *currQueue = list_entry(currLinks, wait_queue_t, task_list); \ wake_up_process(currQueue->task); \ } \ spin_unlock(&(x)->wait.lock); \ }) # else # define compat_complete_all complete_all # endif /* * Completions via waitqueues. */ #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0) /* * Kernel completions in 2.4.9 and beyond use a counter and a waitqueue, and * our implementation is quite similar. Because __wake_up_common() is not * exported, our implementations of compat_complete() and compat_complete_all() * are somewhat racy: the counter is incremented outside of the waitqueue's * lock. * * As a result, our completion cannot guarantee in-order wake ups. For example, * suppose thread A is entering compat_complete(), thread B is sleeping inside * compat_wait_for_completion(), and thread C is just now entering * compat_wait_for_completion(). If Thread A is scheduled first and increments * the counter, then gets swapped out, thread C may get scheduled and will * quickly go through compat_wait_for_completion() (since done != 0) while * thread B continues to sleep, even though thread B should have been the one * to wake up. */ #include #include "compat_sched.h" #include "compat_list.h" #include // for lock_kernel()/unlock_kernel() #include "compat_wait.h" typedef struct compat_completion { unsigned int done; wait_queue_head_t wq; } compat_completion; #define compat_init_completion(comp) do { \ (comp)->done = 0; \ init_waitqueue_head(&(comp)->wq); \ } while (0) #define COMPAT_DECLARE_COMPLETION(comp) \ compat_completion comp = { \ .done = 0, \ .wq = __WAIT_QUEUE_HEAD_INITIALIZER((comp).wq), \ } /* * Locking and unlocking the kernel lock here ensures that the thread * is no longer running in module code: compat_complete_and_exit * performs the sequence { lock_kernel(); up(comp); compat_exit(); }, with * the final unlock_kernel performed implicitly by the resident kernel * in do_exit. */ #define compat_wait_for_completion(comp) do { \ spin_lock_irq(&(comp)->wq.lock); \ if (!(comp)->done) { \ DECLARE_WAITQUEUE(wait, current); \ wait.flags |= WQ_FLAG_EXCLUSIVE; \ __add_wait_queue_tail(&(comp)->wq, &wait); \ do { \ __set_current_state(TASK_UNINTERRUPTIBLE); \ spin_unlock_irq(&(comp)->wq.lock); \ schedule(); \ spin_lock_irq(&(comp)->wq.lock); \ } while (!(comp)->done); \ __remove_wait_queue(&(comp)->wq, &wait); \ } \ (comp)->done--; \ spin_unlock_irq(&(comp)->wq.lock); \ lock_kernel(); \ unlock_kernel(); \ } while (0) /* XXX: I don't think I need to touch the BKL. */ #define compat_complete(comp) do { \ unsigned long flags; \ spin_lock_irqsave(&(comp)->wq.lock, flags); \ (comp)->done++; \ spin_unlock_irqrestore(&(comp)->wq.lock, flags); \ wake_up(&(comp)->wq); \ } while (0) #define compat_complete_all(comp) do { \ unsigned long flags; \ spin_lock_irqsave(&(comp)->wq.lock, flags); \ (comp)->done += UINT_MAX / 2; \ spin_unlock_irqrestore(&(comp)->wq.lock, flags); \ wake_up_all(&(comp)->wq); \ } while (0) /* * Completions via semaphores. */ #else #include "compat_semaphore.h" #define compat_completion struct semaphore #define compat_init_completion(comp) init_MUTEX_LOCKED(comp) #define COMPAT_DECLARE_COMPLETION(comp) DECLARE_MUTEX_LOCKED(comp) #define compat_wait_for_completion(comp) do { \ down(comp); \ lock_kernel(); \ unlock_kernel(); \ } while (0) #define compat_complete(comp) up(comp) #endif #endif /* __COMPAT_COMPLETION_H__ */ vmhgfs-only/compat_dcache.h0000444000000000000000000000400412025726746014757 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ #ifndef __COMPAT_DCACHE_H__ # define __COMPAT_DCACHE_H__ #include /* * per-dentry locking was born in 2.5.62. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 62) #define compat_lock_dentry(dentry) spin_lock(&dentry->d_lock) #define compat_unlock_dentry(dentry) spin_unlock(&dentry->d_lock) #else #define compat_lock_dentry(dentry) do {} while (0) #define compat_unlock_dentry(dentry) do {} while (0) #endif /* * d_alloc_name was born in 2.6.10. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10) #define compat_d_alloc_name(parent, s) d_alloc_name(parent, s) #else #define compat_d_alloc_name(parent, s) \ ({ \ struct qstr q; \ q.name = s; \ q.len = strlen(s); \ q.hash = full_name_hash(q.name, q.len); \ d_alloc(parent, &q); \ }) #endif #endif /* __COMPAT_DCACHE_H__ */ vmhgfs-only/compat_file.h0000444000000000000000000000352312025726746014474 0ustar rootroot/********************************************************* * Copyright (C) 2002 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 * *********************************************************/ #ifndef __COMPAT_FILE_H__ # define __COMPAT_FILE_H__ /* The fput() API is modified in 2.2.0 --hpreg */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 2, 0) # define compat_fput(_file) fput(_file) #else # define compat_fput(_file) fput(_file, (_file)->f_inode) #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0) # define compat_get_file(_file) get_file(_file) # define compat_file_count(_file) file_count(_file) #else # define compat_get_file(_file) (_file)->f_count++ # define compat_file_count(_file) (_file)->f_count #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 2, 4) # define compat_filp_close(_file, _files) filp_close(_file, _files) #else static inline void compat_filp_close(struct file* filp, fl_owner_t files) { if (filp->f_op && filp->f_op->flush) { filp->f_op->flush(filp); } /* * Hopefully there are no locks to release on this filp. * locks_remove_posix is not exported so we cannot use it... */ fput(filp); } #endif #endif /* __COMPAT_FILE_H__ */ vmhgfs-only/compat_fs.h0000444000000000000000000002317212025726746014167 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ #ifndef __COMPAT_FS_H__ # define __COMPAT_FS_H__ #include /* * 2.6.5+ kernels define FS_BINARY_MOUNTDATA. Since it didn't exist and * wasn't used prior, it's safe to define it to zero. */ #ifndef FS_BINARY_MOUNTDATA #define FS_BINARY_MOUNTDATA 0 #endif /* * MAX_LFS_FILESIZE wasn't defined until 2.5.4. */ #ifndef MAX_LFS_FILESIZE # include # if BITS_PER_LONG == 32 # define MAX_LFS_FILESIZE (((u64)PAGE_CACHE_SIZE << (BITS_PER_LONG - 1)) - 1) # elif BITS_PER_LONG == 64 # define MAX_LFS_FILESIZE 0x7fffffffffffffffUL # endif #endif /* * sendfile as a VFS op was born in 2.5.30. Unfortunately, it also changed * signatures, first in 2.5.47, then again in 2.5.70, then again in 2.6.8. * Luckily, the 2.6.8+ signature is the same as the 2.5.47 signature. And * as of 2.6.23-rc1 sendfile is gone, replaced by splice_read... * * Let's not support sendfile from 2.5.30 to 2.5.47, because the 2.5.30 * signature is much different and file_send_actor isn't externed. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) #define VMW_SENDFILE_NONE #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 8) #define VMW_SENDFILE_NEW #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 70) #define VMW_SENDFILE_OLD #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 47) #define VMW_SENDFILE_NEW #else #define VMW_SENDFILE_NONE #endif /* * splice_read is there since 2.6.17, but let's avoid 2.6.17-rcX kernels... * After all nobody is using splice system call until 2.6.23 using it to * implement sendfile. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18) #define VMW_SPLICE_READ 1 #endif /* * Filesystems wishing to use generic page cache read/write routines are * supposed to implement aio_read and aio_write (calling into * generic_file_aio_read() and generic_file_aio_write() if necessary). * * The VFS exports do_sync_read() and do_sync_write() as the "new" * generic_file_read() and generic_file_write(), but filesystems need not * actually implement read and write- the VFS will automatically call * do_sync_write() and do_sync_read() when applications invoke the standard * read() and write() system calls. * * In 2.6.19, generic_file_read() and generic_file_write() were removed, * necessitating this change. AIO dates as far back as 2.5.42, but the API has * changed over time, so for simplicity, we'll only enable it from 2.6.19 and * on. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) # define VMW_USE_AIO #endif /* * The alloc_inode and destroy_inode VFS ops didn't exist prior to 2.4.21. * Without these functions, file systems can't embed inodes. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 21) # define VMW_EMBED_INODE #endif /* * iget() was removed from the VFS as of 2.6.25-rc1. The replacement for iget() * is iget_locked() which was added in 2.5.17. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 17) # define VMW_USE_IGET_LOCKED #endif /* * parent_ino was born in 2.5.5. For older kernels, let's use 2.5.5 * implementation. It uses the dcache lock which is OK because per-dentry * locking appeared after 2.5.5. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 5) #define compat_parent_ino(dentry) parent_ino(dentry) #else #define compat_parent_ino(dentry) \ ({ \ ino_t res; \ spin_lock(&dcache_lock); \ res = dentry->d_parent->d_inode->i_ino; \ spin_unlock(&dcache_lock); \ res; \ }) #endif /* * putname changed to __putname in 2.6.6. */ #define compat___getname() __getname() #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 6) #define compat___putname(name) putname(name) #else #define compat___putname(name) __putname(name) #endif /* * inc_nlink, drop_nlink, and clear_nlink were added in 2.6.19. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) #define compat_inc_nlink(inode) ((inode)->i_nlink++) #define compat_drop_nlink(inode) ((inode)->i_nlink--) #define compat_clear_nlink(inode) ((inode)->i_nlink = 0) #else #define compat_inc_nlink(inode) inc_nlink(inode) #define compat_drop_nlink(inode) drop_nlink(inode) #define compat_clear_nlink(inode) clear_nlink(inode) #endif /* * i_size_write and i_size_read were introduced in 2.6.0-test1 * (though we'll look for them as of 2.6.1). They employ slightly different * locking in order to guarantee atomicity, depending on the length of a long, * whether the kernel is SMP, or whether the kernel is preemptible. Prior to * i_size_write and i_size_read, there was no such locking, so that's the * behavior we'll emulate. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 1) #define compat_i_size_read(inode) ((inode)->i_size) #define compat_i_size_write(inode, size) ((inode)->i_size = size) #else #define compat_i_size_read(inode) i_size_read(inode) #define compat_i_size_write(inode, size) i_size_write(inode, size) #endif /* * filemap_fdatawrite was introduced in 2.5.12. Prior to that, modules used * filemap_fdatasync instead. In 2.4.18, both filemap_fdatawrite and * filemap_fdatawait began returning status codes. Prior to that, they were * void functions, so we'll just have them return 0. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 18) #define compat_filemap_fdatawrite(mapping) \ ({ \ int result = 0; \ filemap_fdatasync(mapping); \ result; \ }) #define compat_filemap_fdatawait(mapping) \ ({ \ int result = 0; \ filemap_fdatawait(mapping); \ result; \ }) #elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 12) #define compat_filemap_fdatawrite(mapping) filemap_fdatasync(mapping) #define compat_filemap_fdatawait(mapping) filemap_fdatawait(mapping) #else #define compat_filemap_fdatawrite(mapping) filemap_fdatawrite(mapping) #define compat_filemap_fdatawait(mapping) filemap_fdatawait(mapping) #endif /* * filemap_write_and_wait was introduced in 2.6.6 and exported for module use * in 2.6.16. It's really just a simple wrapper around filemap_fdatawrite and * and filemap_fdatawait, which initiates a flush of all dirty pages, then * waits for the pages to flush. The implementation here is a simplified form * of the one found in 2.6.20-rc3. * * Unfortunately, it just isn't possible to implement this prior to 2.4.5, when * neither filemap_fdatawait nor filemap_fdatasync were exported for module * use. So we'll define it out and hope for the best. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 5) #define compat_filemap_write_and_wait(mapping) #elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 16) #define compat_filemap_write_and_wait(mapping) \ ({ \ int result = 0; \ if (mapping->nrpages) { \ result = compat_filemap_fdatawrite(mapping); \ if (result != -EIO) { \ int result2 = compat_filemap_fdatawait(mapping); \ if (!result) { \ result = result2; \ } \ } \ } \ result; \ }) #else #define compat_filemap_write_and_wait(mapping) filemap_write_and_wait(mapping) #endif /* * invalidate_remote_inode was introduced in 2.6.0-test5. Prior to that, * filesystems wishing to invalidate pages belonging to an inode called * invalidate_inode_pages. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) #define compat_invalidate_remote_inode(inode) invalidate_inode_pages(inode) #else #define compat_invalidate_remote_inode(inode) invalidate_remote_inode(inode) #endif #endif /* __COMPAT_FS_H__ */ vmhgfs-only/compat_highmem.h0000444000000000000000000000255712025726746015201 0ustar rootroot/********************************************************* * Copyright (C) 2002 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 * *********************************************************/ #ifndef __COMPAT_HIGHMEM_H__ # define __COMPAT_HIGHMEM_H__ /* * BIGMEM (4 GB) support appeared in 2.3.16: kmap() API added * HIGHMEM (4 GB + 64 GB) support appeared in 2.3.23: kmap() API modified * In 2.3.27, kmap() API modified again * * --hpreg */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 27) # include #else /* For page_address --hpreg */ # include # define kmap(_page) (void*)page_address(_page) # define kunmap(_page) #endif #endif /* __COMPAT_HIGHMEM_H__ */ vmhgfs-only/compat_kernel.h0000444000000000000000000000514312025726746015035 0ustar rootroot/********************************************************* * Copyright (C) 2004 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 * *********************************************************/ #ifndef __COMPAT_KERNEL_H__ # define __COMPAT_KERNEL_H__ #include #include /* * container_of was introduced in 2.5.28 but it's easier to check like this. */ #ifndef container_of #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) #endif /* * wait_for_completion and friends did not exist before 2.4.9. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 9) #define compat_complete_and_exit(comp, status) complete_and_exit(comp, status) #else #include "compat_completion.h" /* * Used by _syscallX macros. Note that this is global variable, so * do not rely on its contents too much. As exit() is only function * we use, and we never check return value from exit(), we have * no problem... */ extern int errno; /* * compat_exit() provides an access to the exit() function. It must * be named compat_exit(), as exit() (with different signature) is * provided by x86-64, arm and other (but not by i386). */ #define __NR_compat_exit __NR_exit static inline _syscall1(int, compat_exit, int, exit_code); /* * See compat_wait_for_completion in compat_completion.h. * compat_exit implicitly performs an unlock_kernel, in resident code, * ensuring that the thread is no longer running in module code when the * module is unloaded. */ #define compat_complete_and_exit(comp, status) do { \ lock_kernel(); \ compat_complete(comp); \ compat_exit(status); \ } while (0) #endif /* * vsnprintf became available in 2.4.10. For older kernels, just fall back on * vsprintf. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 10) #define vsnprintf(str, size, fmt, args) vsprintf(str, fmt, args) #endif #endif /* __COMPAT_KERNEL_H__ */ vmhgfs-only/compat_kthread.h0000444000000000000000000001760212025726746015202 0ustar rootroot/********************************************************* * 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 * *********************************************************/ #ifndef __COMPAT_KTHREAD_H__ # define __COMPAT_KTHREAD_H__ /* * The kthread interface for managing kernel threads appeared in 2.6.4, but was * only exported for module use in 2.6.7. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 7) # include # define COMPAT_KTHREAD_DECLARE_STOP_INFO() # define compat_kthread_stop(_tsk) kthread_stop(_tsk) # define compat_kthread_should_stop() kthread_should_stop() # define compat_kthread_run(_fn, _data, _namefmt, ...) \ kthread_run(_fn, _data, _namefmt, ## __VA_ARGS__) # define compat_kthread_create(_fn, _data, _namefmt, ...) \ kthread_create(_fn, _data, _namefmt, ## __VA_ARGS__) #else /* * When the kthread interface isn't available, we do our best to emulate it, * with a few notable exceptions: * * 1: We use semaphores instead of mutexes for locking, because mutexes aren't * available in kernels where kthread isn't available. * 2: The real kthread interface uses the kthreadd kernel_thread to broker the * creation of new kernel threads. This makes sense because kthreadd is part * of the kernel, but doesn't make sense at all in the context of an * individual module. So in our emulation, thread creation occurs in the * context of a kthread_create call. * 3: Because kthreadd is responsible for creating kernel threads in the real * kthread interface, there's no need to explicitly reparent any of them. We * aren't using kthreadd, so we call daemonize to reparent, which also sets * the name of the new kernel thread. That's why we don't set the name as * the real kthread interface does (within kthread_create). Furthermore, to * get the name to daemonize, we're forced to pass it through the * kthread_start_info struct. * 4: Since our interface isn't in the kernel proper, we can't make use of * get_task_struct/put_task_struct so as to acquire references to kernel * threads that we're managing. To prevent races, we use an extra completion * when stopping kernel threads. See the comments in compat_kthread_stop for * more details. * * Like the real kthread interface, ours must be globally available so that we * can emulate functions like kthread_should_stop without using different * signatures. */ # include "compat_completion.h" # include "compat_kernel.h" # include "compat_sched.h" struct compat_kthread_start_info { int (*fn)(void *); void *data; compat_completion created; char comm[TASK_COMM_LEN]; }; struct compat_kthread_stop_info { struct semaphore lock; struct task_struct *task; compat_completion woken; compat_completion stopped; int ret; }; extern struct compat_kthread_stop_info compat_kthread_stop_info; # define COMPAT_KTHREAD_DECLARE_STOP_INFO() \ struct compat_kthread_stop_info compat_kthread_stop_info = { \ .lock = __SEMAPHORE_INITIALIZER(compat_kthread_stop_info.lock, 1), \ .task = NULL, \ } static inline int compat_kthread_should_stop(void) { return (compat_kthread_stop_info.task == current); } static inline int compat_kthread_stop(struct task_struct *_task) { int ret; down(&compat_kthread_stop_info.lock); /* * We use a write memory barrier to ensure that all CPUs see _task after * the completions have been initialized. * * There's a race between kernel threads managed by kthread and the upcoming * call to wake_up_process. If the kernel thread wakes up after we set task * but before the call to wake_up_process, the thread's call to * compat_kthread_should_stop will return true and the thread will exit. At * that point, the call to wake_up_process will be on a dead task_struct. * * XXX: The real kthread interface protects against this race by grabbing * and releasing a reference to _task. We don't have that luxury, because * there is a range of kernels where put_task_struct isn't exported to * modules. In fact, no other modules call get_task_struct or * put_task_struct, so to do so from this context may be unwise. Instead, * we'll use an extra completion to ensure that the kernel thread only exits * after wake_up_process has been called. */ compat_init_completion(&compat_kthread_stop_info.woken); compat_init_completion(&compat_kthread_stop_info.stopped); smp_wmb(); compat_kthread_stop_info.task = _task; wake_up_process(_task); compat_complete(&compat_kthread_stop_info.woken); compat_wait_for_completion(&compat_kthread_stop_info.stopped); compat_kthread_stop_info.task = NULL; ret = compat_kthread_stop_info.ret; up(&compat_kthread_stop_info.lock); return ret; } # define compat_kthread_run(_fn, _data, _namefmt, ...) \ ({ \ struct task_struct *tsk; \ tsk = compat_kthread_create(_fn, _data, _namefmt, ## __VA_ARGS__); \ if (!IS_ERR(tsk)) { \ wake_up_process(tsk); \ } \ tsk; \ }) static inline int compat_kthread(void *_data) { int ret = -EINTR; struct compat_kthread_start_info *info; int (*fn)(void *data); void *data; info = (struct compat_kthread_start_info *)_data; fn = info->fn; data = info->data; compat_daemonize(info->comm); __set_current_state(TASK_UNINTERRUPTIBLE); compat_complete(&info->created); schedule(); if (!compat_kthread_should_stop()) { ret = fn(data); } if (compat_kthread_should_stop()) { compat_wait_for_completion(&compat_kthread_stop_info.woken); compat_kthread_stop_info.ret = ret; compat_complete_and_exit(&compat_kthread_stop_info.stopped, 0); BUG(); } return 0; } static inline struct task_struct * compat_kthread_create(int (*_fn)(void *data), void *_data, const char _namefmt[], ...) { pid_t pid; struct task_struct *task = NULL; struct compat_kthread_start_info info; va_list args; info.fn = _fn; info.data = _data; compat_init_completion(&info.created); va_start(args, _namefmt); vsnprintf(info.comm, sizeof info.comm, _namefmt, args); va_end(args); pid = kernel_thread(compat_kthread, &info, CLONE_KERNEL); if (pid >= 0) { compat_wait_for_completion(&info.created); /* * find_task_by_pid must be called with tasklist_lock held or under * rcu_read_lock. As the latter doesn't exist in old kernels, we use the * former for convenience. */ read_lock(&tasklist_lock); task = find_task_by_pid(pid); read_unlock(&tasklist_lock); /* XXX: Do we need to get a reference on task? */ } return task; } #endif #endif /* __COMPAT_KTHREAD_H__ */ vmhgfs-only/compat_list.h0000444000000000000000000000357512025726746014537 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ #ifndef __COMPAT_LIST_H__ # define __COMPAT_LIST_H__ #include /* * list_add_tail is with us since 2.4.0, or something like that. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0) #define list_add_tail(newe, head) do { \ struct list_head *__h = (head); \ __list_add((newe), __h->prev, __h); \ } while (0) #endif /* * list_for_each_safe() showed up in 2.4.10, but it may be backported so we * just check for its existence. */ #ifndef list_for_each_safe # define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) #endif /* * list_for_each_entry() showed up in 2.4.20, but it may be backported so we * just check for its existence. */ #ifndef list_for_each_entry # define list_for_each_entry(pos, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member); \ &pos->member != (head); \ pos = list_entry(pos->member.next, typeof(*pos), member)) #endif #endif /* __COMPAT_LIST_H__ */ vmhgfs-only/compat_mm.h0000444000000000000000000001020712025726746014163 0ustar rootroot/********************************************************* * Copyright (C) 2002 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 * *********************************************************/ #ifndef __COMPAT_MM_H__ # define __COMPAT_MM_H__ #include /* The get_page() API appeared in 2.3.7 --hpreg */ /* Sometime during development it became function instead of macro --petr */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0) && !defined(get_page) # define get_page(_page) atomic_inc(&(_page)->count) /* The __free_page() API is exported in 2.1.67 --hpreg */ # if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 1, 67) # define put_page __free_page # else # include "compat_page.h" # define page_to_phys(_page) (page_to_pfn(_page) << PAGE_SHIFT) # define put_page(_page) free_page(page_to_phys(_page)) # endif #endif /* page_count() is 2.4.0 invention. Unfortunately unavailable in some RedHat * kernels (for example 2.4.21-4-RHEL3). */ /* It is function since 2.6.0, and hopefully RedHat will not play silly games * with mm_inline.h again... */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) && !defined(page_count) # define page_count(page) atomic_read(&(page)->count) #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0) # define compat_vm_pgoff(vma) ((vma)->vm_offset >> PAGE_SHIFT) static inline unsigned long compat_do_mmap_pgoff(struct file *file, unsigned long addr, unsigned long len, unsigned long prot, unsigned long flag, unsigned long pgoff) { unsigned long ret = -EINVAL; if (pgoff < 1 << (32 - PAGE_SHIFT)) { ret = do_mmap(file, addr, len, prot, flag, pgoff << PAGE_SHIFT); } return ret; } #else # define compat_vm_pgoff(vma) (vma)->vm_pgoff # ifdef VMW_SKAS_MMAP # define compat_do_mmap_pgoff(f, a, l, p, g, o) \ do_mmap_pgoff(current->mm, f, a, l, p, g, o) # else # define compat_do_mmap_pgoff(f, a, l, p, g, o) \ do_mmap_pgoff(f, a, l, p, g, o) # endif #endif /* 2.2.x uses 0 instead of some define */ #ifndef NOPAGE_SIGBUS #define NOPAGE_SIGBUS (0) #endif /* 2.2.x does not have HIGHMEM support */ #ifndef GFP_HIGHUSER #define GFP_HIGHUSER (GFP_USER) #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0) #include "compat_page.h" static inline struct page * alloc_pages(unsigned int gfp_mask, unsigned int order) { unsigned long addr; addr = __get_free_pages(gfp_mask, order); if (!addr) { return NULL; } return virt_to_page(addr); } #define alloc_page(gfp_mask) alloc_pages(gfp_mask, 0) #endif /* * In 2.4.14, the logic behind the UnlockPage macro was moved to the * unlock_page() function. Later (in 2.5.12), the UnlockPage macro was removed * altogether, and nowadays everyone uses unlock_page(). */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 14) #define compat_unlock_page(page) UnlockPage(page) #else #define compat_unlock_page(page) unlock_page(page) #endif /* * In 2.4.10, vmtruncate was changed from returning void to returning int. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 10) #define compat_vmtruncate(inode, size) \ ({ \ int result = 0; \ vmtruncate(inode, size); \ result; \ }) #else #define compat_vmtruncate(inode, size) vmtruncate(inode, size) #endif #endif /* __COMPAT_MM_H__ */ vmhgfs-only/compat_module.h0000444000000000000000000000437212025726746015045 0ustar rootroot/********************************************************* * Copyright (C) 2007 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 * *********************************************************/ /* * compat_module.h -- */ #ifndef __COMPAT_MODULE_H__ # define __COMPAT_MODULE_H__ #include /* * Modules wishing to use the GPL license are required to include a * MODULE_LICENSE definition in their module source as of 2.4.10. */ #ifndef MODULE_LICENSE #define MODULE_LICENSE(license) #endif /* * To make use of our own home-brewed MODULE_INFO, we need macros to * concatenate two expressions to "__mod_", and and to convert an * expression into a string. I'm sure we've got these in our codebase, * but I'd rather not introduce such a dependency in a compat header. */ #ifndef __module_cat #define __module_cat_1(a, b) __mod_ ## a ## b #define __module_cat(a, b) __module_cat_1(a, b) #endif #ifndef __stringify #define __stringify_1(x) #x #define __stringify(x) __stringify_1(x) #endif /* * MODULE_INFO was born in 2.5.69. */ #ifndef MODULE_INFO #define MODULE_INFO(tag, info) \ static const char __module_cat(tag, __LINE__)[] \ __attribute__((section(".modinfo"), unused)) = __stringify(tag) "=" info #endif /* * MODULE_VERSION was born in 2.6.4. The earlier form appends a long "\0xxx" * string to the module's version, but that was removed in 2.6.10, so we'll * ignore it in our wrapper. */ #ifndef MODULE_VERSION #define MODULE_VERSION(_version) MODULE_INFO(version, _version) #endif #endif /* __COMPAT_MODULE_H__ */ vmhgfs-only/compat_namei.h0000444000000000000000000000410612025726746014644 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ #ifndef __COMPAT_NAMEI_H__ # define __COMPAT_NAMEI_H__ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 18) #include #endif /* * In 2.6.25-rc2, dentry and mount objects were removed from the nameidata * struct. They were both replaced with a struct path. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) #define compat_vmw_nd_to_dentry(nd) (nd).path.dentry #else #define compat_vmw_nd_to_dentry(nd) (nd).dentry #endif /* In 2.6.25-rc2, path_release(&nd) was replaced with path_put(&nd.path). */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) #define compat_path_release(nd) path_put(&(nd)->path) #else #define compat_path_release(nd) path_release(nd) #endif /* path_lookup was exported in 2.4.25 */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 25) #define compat_path_lookup(path, flags, nd) path_lookup(path, flags, nd) #else #define compat_path_lookup(path, flags, nd) \ ({ \ int ret = 0; \ if (path_init(path, flags, nd)) { \ ret = path_walk(path, nd); \ } \ ret; \ }) #endif #endif /* __COMPAT_NAMEI_H__ */ vmhgfs-only/compat_page.h0000444000000000000000000000466312025726746014477 0ustar rootroot/********************************************************* * Copyright (C) 2002 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 * *********************************************************/ #ifndef __COMPAT_PAGE_H__ # define __COMPAT_PAGE_H__ #include #include /* The pfn_to_page() API appeared in 2.5.14 and changed to function during 2.6.x */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) && !defined(pfn_to_page) # define pfn_to_page(_pfn) (mem_map + (_pfn)) # define page_to_pfn(_page) ((_page) - mem_map) #endif /* The virt_to_page() API appeared in 2.4.0 --hpreg */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0) && !defined(virt_to_page) # define virt_to_page(_kvAddr) pfn_to_page(MAP_NR(_kvAddr)) #endif /* * The get_order() API appeared at some point in 2.3.x, and was then backported * in 2.2.17-21mdk and in the stock 2.2.18. Because we can only detect its * definition through makefile tricks, we provide our own for now --hpreg */ static inline int compat_get_order(unsigned long size) // IN { int order; size = (size - 1) >> (PAGE_SHIFT - 1); order = -1; do { size >>= 1; order++; } while (size); return order; } /* * BUG() was added to in 2.2.18, and was moved to * in 2.5.58. * * XXX: Technically, this belongs in some sort of "compat_asm_page.h" file, but * since our compatibility wrappers don't distinguish between and * , putting it here is reasonable. */ #ifndef BUG #define BUG() do { \ printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \ __asm__ __volatile__(".byte 0x0f,0x0b"); \ } while (0) #endif #endif /* __COMPAT_PAGE_H__ */ vmhgfs-only/compat_page-flags.h0000444000000000000000000000503712025726746015565 0ustar rootroot/********************************************************* * Copyright (C) 2007 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 * *********************************************************/ #ifndef __COMPAT_PAGE_FLAGS_H__ # define __COMPAT_PAGE_FLAGS_H__ /* No page-flags.h prior to 2.5.12. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 12) # include #endif /* * The pgoff_t type was introduced in 2.5.20, but we'll look for it by * definition since it's more convenient. Note that we want to avoid a * situation where, in the future, a #define is changed to a typedef, * so if pgoff_t is not defined in some future kernel, we won't define it. */ #if !defined(pgoff_t) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) #define pgoff_t unsigned long #endif /* * set_page_writeback() was introduced in 2.6.6. Prior to that, callers were * using the SetPageWriteback() macro directly, so that's what we'll use. * Prior to 2.5.12, the writeback bit didn't exist, so we don't need to do * anything. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 12) #define compat_set_page_writeback(page) #elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 6) #define compat_set_page_writeback(page) SetPageWriteback(page) #else #define compat_set_page_writeback(page) set_page_writeback(page) #endif /* * end_page_writeback() was introduced in 2.5.12. Prior to that, it looks like * there was no page writeback bit, and everything the function accomplished * was done by unlock_page(), so we'll define it out. * * Note that we could just #define end_page_writeback to nothing and avoid * needing the compat_ prefix, but this is more complete with respect to * compat_set_page_writeback. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 12) #define compat_end_page_writeback(page) #else #define compat_end_page_writeback(page) end_page_writeback(page) #endif #endif /* __COMPAT_PAGE_FLAGS_H__ */ vmhgfs-only/compat_sched.h0000444000000000000000000002425212025726746014645 0ustar rootroot/********************************************************* * Copyright (C) 2002 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 * *********************************************************/ #ifndef __COMPAT_SCHED_H__ # define __COMPAT_SCHED_H__ #include /* CLONE_KERNEL available in 2.5.35 and higher. */ #ifndef CLONE_KERNEL #define CLONE_KERNEL CLONE_FILES | CLONE_FS | CLONE_SIGHAND #endif /* TASK_COMM_LEN become available in 2.6.11. */ #ifndef TASK_COMM_LEN #define TASK_COMM_LEN 16 #endif /* The capable() API appeared in 2.1.92 --hpreg */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 1, 92) # define capable(_capability) suser() #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 0) # define need_resched() need_resched #elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 3) # define need_resched() (current->need_resched) #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 3) # define cond_resched() (need_resched() ? schedule() : (void) 0) #endif /* Oh well. We need yield... Happy us! */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 20) # ifdef __x86_64__ # define compat_yield() there_is_nothing_like_yield() # else # include # include /* * Used by _syscallX macros. Note that this is global variable, so * do not rely on its contents too much. As exit() is only function * we use, and we never check return value from exit(), we have * no problem... */ extern int errno; /* * compat_exit() provides an access to the exit() function. It must * be named compat_exit(), as exit() (with different signature) is * provided by x86-64, arm and other (but not by i386). */ # define __NR_compat_yield __NR_sched_yield static inline _syscall0(int, compat_yield); # endif #else # define compat_yield() yield() #endif /* * Since 2.5.34 there are two methods to enumerate tasks: * for_each_process(p) { ... } which enumerates only tasks and * do_each_thread(g,t) { ... } while_each_thread(g,t) which enumerates * also threads even if they share same pid. */ #ifndef for_each_process # define for_each_process(p) for_each_task(p) #endif #ifndef do_each_thread # define do_each_thread(g, t) for_each_task(g) { t = g; do # define while_each_thread(g, t) while (0) } #endif /* * Lock for signal mask is moving target... */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 40) && defined(CLONE_PID) /* 2.4.x without NPTL patches or early 2.5.x */ #define compat_sigmask_lock sigmask_lock #define compat_dequeue_signal_current(siginfo_ptr) \ dequeue_signal(¤t->blocked, (siginfo_ptr)) #elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 60) && !defined(INIT_SIGHAND) /* RedHat's 2.4.x with first version of NPTL support, or 2.5.40 to 2.5.59 */ #define compat_sigmask_lock sig->siglock #define compat_dequeue_signal_current(siginfo_ptr) \ dequeue_signal(¤t->blocked, (siginfo_ptr)) #else /* RedHat's 2.4.x with second version of NPTL support, or 2.5.60+. */ #define compat_sigmask_lock sighand->siglock #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) #define compat_dequeue_signal_current(siginfo_ptr) \ dequeue_signal(¤t->blocked, (siginfo_ptr)) #else #define compat_dequeue_signal_current(siginfo_ptr) \ dequeue_signal(current, ¤t->blocked, (siginfo_ptr)) #endif #endif /* * recalc_sigpending() had task argument in the past */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 29) && defined(CLONE_PID) /* 2.4.x without NPTL patches or early 2.5.x */ #define compat_recalc_sigpending() recalc_sigpending(current) #else /* RedHat's 2.4.x with NPTL support, or 2.5.29+ */ #define compat_recalc_sigpending() recalc_sigpending() #endif /* * reparent_to_init() was introduced in 2.4.8. In 2.5.38 (or possibly * earlier, but later than 2.5.31) a call to it was added into * daemonize(), so compat_daemonize no longer needs to call it. * * In 2.4.x kernels reparent_to_init() forgets to do correct refcounting * on current->user. It is better to count one too many than one too few... */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 8) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 38) #define compat_reparent_to_init() do { \ reparent_to_init(); \ atomic_inc(¤t->user->__count); \ } while (0) #else #define compat_reparent_to_init() do {} while (0) #endif /* * daemonize appeared in 2.2.18. Except 2.2.17-4-RH7.0, which has it too. * Fortunately 2.2.17-4-RH7.0 uses versioned symbols, so we can check * its existence with defined(). */ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 18)) && !defined(daemonize) static inline void daemonize(void) { struct fs_struct *fs; exit_mm(current); current->session = 1; current->pgrp = 1; exit_fs(current); fs = init_task.fs; current->fs = fs; atomic_inc(&fs->count); } #endif /* * flush_signals acquires sighand->siglock since 2.5.61... Verify RH's kernels! */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 61) #define compat_flush_signals(task) do { \ spin_lock_irq(&task->compat_sigmask_lock); \ flush_signals(task); \ spin_unlock_irq(&task->compat_sigmask_lock); \ } while (0) #else #define compat_flush_signals(task) flush_signals(task) #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 61) #define compat_allow_signal(signr) do { \ spin_lock_irq(¤t->compat_sigmask_lock); \ sigdelset(¤t->blocked, signr); \ compat_recalc_sigpending(); \ spin_unlock_irq(¤t->compat_sigmask_lock); \ } while (0) #else #define compat_allow_signal(signr) allow_signal(signr) #endif /* * daemonize can set process name since 2.5.61. Prior to 2.5.61, daemonize * didn't block signals on our behalf. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 61) #define compat_daemonize(x...) \ ({ \ /* Beware! No snprintf here, so verify arguments! */ \ sprintf(current->comm, x); \ \ /* Block all signals. */ \ spin_lock_irq(¤t->compat_sigmask_lock); \ sigfillset(¤t->blocked); \ compat_recalc_sigpending(); \ spin_unlock_irq(¤t->compat_sigmask_lock); \ compat_flush_signals(current); \ \ daemonize(); \ compat_reparent_to_init(); \ }) #else #define compat_daemonize(x...) daemonize(x) #endif /* * set priority for specified thread. Exists on 2.6.x kernels and some * 2.4.x vendor's kernels. */ #if defined(VMW_HAVE_SET_USER_NICE) #define compat_set_user_nice(task, n) set_user_nice((task), (n)) #elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0) #define compat_set_user_nice(task, n) do { (task)->priority = 20 - (n); } while (0) #elif !defined(VMW_HAVE_SET_USER_NICE) #define compat_set_user_nice(task, n) do { (task)->nice = (n); } while (0) #endif /* * try to freeze a process. For kernels 2.6.11 or newer, we know how to choose * the interface. The problem is that the oldest interface, introduced in * 2.5.18, was backported to 2.4.x kernels. So if we're older than 2.6.11, * we'll decide what to do based on whether or not swsusp was configured * for the kernel. For kernels 2.6.20 and newer, we'll also need to include * freezer.h since the try_to_freeze definition was pulled out of sched.h. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) #include #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13) || defined(VMW_TL10S64_WORKAROUND) #define compat_try_to_freeze() try_to_freeze() #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) #define compat_try_to_freeze() try_to_freeze(PF_FREEZE) #elif defined(CONFIG_SOFTWARE_SUSPEND) || defined(CONFIG_SOFTWARE_SUSPEND2) #include "compat_mm.h" #include #include static inline int compat_try_to_freeze(void) { if (current->flags & PF_FREEZE) { refrigerator(PF_FREEZE); return 1; } else { return 0; } } #else static inline int compat_try_to_freeze(void) { return 0; } #endif /* * As of 2.6.23-rc1, kernel threads are no longer freezable by * default. Instead, kernel threads that need to be frozen must opt-in * by calling set_freezable() as soon as the thread is created. */ #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22) #define compat_set_freezable() do { set_freezable(); } while (0) #else #define compat_set_freezable() do {} while (0) #endif /* * Since 2.6.27-rc2 kill_proc() is gone... Replacement (GPL-only!) * API is available since 2.6.19. Use them from 2.6.27-rc1 up. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27) typedef int compat_pid; #define compat_find_get_pid(pid) (pid) #define compat_put_pid(pid) do { } while (0) #define compat_kill_pid(pid, sig, flag) kill_proc(pid, sig, flag) #else typedef struct pid * compat_pid; #define compat_find_get_pid(pid) find_get_pid(pid) #define compat_put_pid(pid) put_pid(pid) #define compat_kill_pid(pid, sig, flag) kill_pid(pid, sig, flag) #endif #endif /* __COMPAT_SCHED_H__ */ vmhgfs-only/compat_semaphore.h0000444000000000000000000000314212025726746015535 0ustar rootroot/********************************************************* * Copyright (C) 2002 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 * *********************************************************/ #ifndef __COMPAT_SEMAPHORE_H__ # define __COMPAT_SEMAPHORE_H__ /* <= 2.6.25 have asm only, 2.6.26 has both, and 2.6.27-rc2+ has linux only. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27) # include #else # include #endif /* * The init_MUTEX_LOCKED() API appeared in 2.2.18, and is also in * 2.2.17-21mdk --hpreg */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 18) #ifndef init_MUTEX_LOCKED #define init_MUTEX_LOCKED(_sem) *(_sem) = MUTEX_LOCKED #endif #ifndef DECLARE_MUTEX #define DECLARE_MUTEX(name) struct semaphore name = MUTEX #endif #ifndef DECLARE_MUTEX_LOCKED #define DECLARE_MUTEX_LOCKED(name) struct semaphore name = MUTEX_LOCKED #endif #endif #endif /* __COMPAT_SEMAPHORE_H__ */ vmhgfs-only/compat_slab.h0000444000000000000000000000665312025726746014505 0ustar rootroot/********************************************************* * Copyright (C) 2005 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 * *********************************************************/ #ifndef __COMPAT_SLAB_H__ # define __COMPAT_SLAB_H__ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 2, 0) # include #else # include #endif /* * Before 2.6.20, kmem_cache_t was the accepted way to refer to a kmem_cache * structure. Prior to 2.6.15, this structure was called kmem_cache_s, and * afterwards it was renamed to kmem_cache. Here we keep things simple and use * the accepted typedef until it became deprecated, at which point we switch * over to the kmem_cache name. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) # define compat_kmem_cache struct kmem_cache #else # define compat_kmem_cache kmem_cache_t #endif /* * Up to 2.6.22 kmem_cache_create has 6 arguments - name, size, alignment, flags, * constructor, and destructor. Then for some time kernel was asserting that * destructor is NULL, and since 2.6.23-pre1 kmem_cache_create takes only 5 * arguments - destructor is gone. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) || defined(VMW_KMEMCR_HAS_DTOR) #define compat_kmem_cache_create(name, size, align, flags, ctor) \ kmem_cache_create(name, size, align, flags, ctor, NULL) #else #define compat_kmem_cache_create(name, size, align, flags, ctor) \ kmem_cache_create(name, size, align, flags, ctor) #endif /* * Up to 2.6.23 kmem_cache constructor has three arguments - pointer to block to * prepare (aka "this"), from which cache it came, and some unused flags. After * 2.6.23 flags were removed, and order of "this" and cache parameters was swapped... * Since 2.6.27-rc2 everything is different again, and ctor has only one argument. * * HAS_3_ARGS has precedence over HAS_2_ARGS if both are defined. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23) && !defined(VMW_KMEMCR_CTOR_HAS_3_ARGS) # define VMW_KMEMCR_CTOR_HAS_3_ARGS #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) && !defined(VMW_KMEMCR_CTOR_HAS_2_ARGS) # define VMW_KMEMCR_CTOR_HAS_2_ARGS #endif #if defined(VMW_KMEMCR_CTOR_HAS_3_ARGS) typedef void compat_kmem_cache_ctor(void *, compat_kmem_cache *, unsigned long); #define COMPAT_KMEM_CACHE_CTOR_ARGS(arg) void *arg, \ compat_kmem_cache *cache, \ unsigned long flags #elif defined(VMW_KMEMCR_CTOR_HAS_2_ARGS) typedef void compat_kmem_cache_ctor(compat_kmem_cache *, void *); #define COMPAT_KMEM_CACHE_CTOR_ARGS(arg) compat_kmem_cache *cache, \ void *arg #else typedef void compat_kmem_cache_ctor(void *); #define COMPAT_KMEM_CACHE_CTOR_ARGS(arg) void *arg #endif #endif /* __COMPAT_SLAB_H__ */ vmhgfs-only/compat_spinlock.h0000444000000000000000000000460612025726746015402 0ustar rootroot/********************************************************* * Copyright (C) 2005 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 * *********************************************************/ #ifndef __COMPAT_SPINLOCK_H__ # define __COMPAT_SPINLOCK_H__ /* * The spin_lock() API appeared in 2.1.25 in asm/smp_lock.h * It moved in 2.1.30 to asm/spinlock.h * It moved again in 2.3.18 to linux/spinlock.h * * --hpreg */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 18) # include #else # if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 1, 30) # include # else typedef struct {} spinlock_t; # define spin_lock_init(lock) # define spin_lock(lock) # define spin_unlock(lock) # define spin_lock_irqsave(lock, flags) do { \ save_flags(flags); \ cli(); \ spin_lock(lock); \ } while (0) # define spin_unlock_irqrestore(lock, flags) do { \ spin_unlock(lock); \ restore_flags(flags); \ } while (0) # endif #endif /* * Preempt support was added during 2.5.x development cycle, and later * it was backported to 2.4.x. In 2.4.x backport these definitions * live in linux/spinlock.h, that's why we put them here (in 2.6.x they * are defined in linux/preempt.h which is included by linux/spinlock.h). */ #ifdef CONFIG_PREEMPT #define compat_preempt_disable() preempt_disable() #define compat_preempt_enable() preempt_enable() #else #define compat_preempt_disable() do { } while (0) #define compat_preempt_enable() do { } while (0) #endif #endif /* __COMPAT_SPINLOCK_H__ */ vmhgfs-only/compat_statfs.h0000444000000000000000000000230612025726746015057 0ustar rootroot/********************************************************* * Copyright (C) 2006 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 * *********************************************************/ #ifndef __COMPAT_STATFS_H__ # define __COMPAT_STATFS_H__ /* vfs.h simply include statfs.h, but it knows what directory statfs.h is in. */ #include /* 2.5.74 renamed struct statfs to kstatfs. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 74) #define compat_kstatfs kstatfs #else #define compat_kstatfs statfs #endif #endif /* __COMPAT_STATFS_H__ */ vmhgfs-only/compat_string.h0000444000000000000000000000356312025726746015067 0ustar rootroot/********************************************************* * Copyright (C) 2007 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 * *********************************************************/ #ifndef __COMPAT_STRING_H__ # define __COMPAT_STRING_H__ #include /* * kstrdup was born in 2.6.13. This implementation is almost identical to the * one found there. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13) #define compat_kstrdup(s, gfp) kstrdup(s, gfp) #else #define compat_kstrdup(s, gfp) \ ({ \ size_t len; \ char *buf; \ len = strlen(s) + 1; \ buf = kmalloc(len, gfp); \ memcpy(buf, s, len); \ buf; \ }) #endif #endif /* __COMPAT_STRING_H__ */ vmhgfs-only/compat_uaccess.h0000444000000000000000000000606212025726746015204 0ustar rootroot/********************************************************* * Copyright (C) 2002 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 * *********************************************************/ #ifndef __COMPAT_UACCESS_H__ # define __COMPAT_UACCESS_H__ /* User space access functions moved in 2.1.7 to asm/uaccess.h --hpreg */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 1, 7) # include #else # include #endif /* get_user() API modified in 2.1.4 to take 2 arguments --hpreg */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 1, 4) # define compat_get_user get_user #else /* * We assign 0 to the variable in case of failure to prevent "`_var' might be * used uninitialized in this function" compiler warnings. I think it is OK, * because the hardware-based version in newer kernels probably has the same * semantics and does not guarantee that the value of _var will not be * modified, should the access fail --hpreg */ # define compat_get_user(_var, _uvAddr) ({ \ int _status; \ \ _status = verify_area(VERIFY_READ, _uvAddr, sizeof(*(_uvAddr))); \ if (_status == 0) { \ (_var) = get_user(_uvAddr); \ } else { \ (_var) = 0; \ } \ _status; \ }) #endif /* * The copy_from_user() API appeared in 2.1.4 * * The emulation is not perfect here, but it is conservative: on failure, we * always return the total size, instead of the potentially smaller faulty * size --hpreg * * Since 2.5.55 copy_from_user() is no longer macro. */ #if !defined(copy_from_user) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 0) # define copy_from_user(_to, _from, _size) ( \ verify_area(VERIFY_READ, _from, _size) \ ? (_size) \ : (memcpy_fromfs(_to, _from, _size), 0) \ ) # define copy_to_user(_to, _from, _size) ( \ verify_area(VERIFY_WRITE, _to, _size) \ ? (_size) \ : (memcpy_tofs(_to, _from, _size), 0) \ ) #endif #endif /* __COMPAT_UACCESS_H__ */ vmhgfs-only/compat_version.h0000444000000000000000000000616512025726746015247 0ustar rootroot/********************************************************* * Copyright (C) 1998 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 * *********************************************************/ #ifndef __COMPAT_VERSION_H__ # define __COMPAT_VERSION_H__ #define INCLUDE_ALLOW_VMMON #define INCLUDE_ALLOW_MODULE #define INCLUDE_ALLOW_VMCORE #define INCLUDE_ALLOW_VMNIXMOD #define INCLUDE_ALLOW_DISTRIBUTE #include "includeCheck.h" #ifndef __linux__ # error "linux-version.h" #endif #include /* Appeared in 2.1.90 --hpreg */ #ifndef KERNEL_VERSION # define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c)) #endif /* * Distinguish relevant classes of Linux kernels. * * The convention is that version X defines all * the KERNEL_Y symbols where Y <= X. * * XXX Do not add more definitions here. This way of doing things does not * scale, and we are going to phase it out soon --hpreg */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 1, 0) # define KERNEL_2_1 #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 2, 0) # define KERNEL_2_2 #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 1) # define KERNEL_2_3_1 #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 15) /* new networking */ # define KERNEL_2_3_15 #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 25) /* new procfs */ # define KERNEL_2_3_25 #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 29) /* even newer procfs */ # define KERNEL_2_3_29 #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 43) /* softnet changes */ # define KERNEL_2_3_43 #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 47) /* more softnet changes */ # define KERNEL_2_3_47 #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 99) /* name in netdevice struct is array and not pointer */ # define KERNEL_2_3_99 #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0) /* New 'owner' member at the beginning of struct file_operations */ # define KERNEL_2_4_0 #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 8) /* New netif_rx_ni() --hpreg */ # define KERNEL_2_4_8 #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 22) /* New vmap() */ # define KERNEL_2_4_22 #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 2) /* New kdev_t, major()/minor() API --hpreg */ # define KERNEL_2_5_2 #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 5) /* New sk_alloc(), pte_offset_map()/pte_unmap() --hpreg */ # define KERNEL_2_5_5 #endif #endif /* __COMPAT_VERSION_H__ */ vmhgfs-only/compat_wait.h0000444000000000000000000001564212025726746014526 0ustar rootroot/********************************************************* * Copyright (C) 2002 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 * *********************************************************/ #ifndef __COMPAT_WAIT_H__ # define __COMPAT_WAIT_H__ #include #include #include #include "compat_file.h" /* * The DECLARE_WAITQUEUE() API appeared in 2.3.1 * It was back ported in 2.2.18 * * --hpreg */ #ifndef DECLARE_WAITQUEUE typedef struct wait_queue *wait_queue_head_t; # define init_waitqueue_head(_headPtr) *(_headPtr) = NULL # define DECLARE_WAITQUEUE(_var, _task) \ struct wait_queue _var = {_task, NULL, } typedef struct wait_queue wait_queue_t; # define init_waitqueue_entry(_wait, _task) ((_wait)->task = (_task)) #endif /* * The 'struct poll_wqueues' appeared in 2.5.48, when global * /dev/epoll interface was added. It was backported to the * 2.4.20-wolk4.0s. */ #ifdef VMW_HAVE_EPOLL // { #define compat_poll_wqueues struct poll_wqueues #else // } { #define compat_poll_wqueues poll_table #endif // } #ifdef VMW_HAVE_EPOLL // { /* If prototype does not match, build will abort here */ extern void poll_initwait(compat_poll_wqueues *); #define compat_poll_initwait(wait, table) ( \ poll_initwait((table)), \ (wait) = &(table)->pt \ ) #define compat_poll_freewait(wait, table) ( \ poll_freewait((table)) \ ) #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0) // { /* If prototype does not match, build will abort here */ extern void poll_initwait(compat_poll_wqueues *); #define compat_poll_initwait(wait, table) ( \ (wait) = (table), \ poll_initwait(wait) \ ) #define compat_poll_freewait(wait, table) ( \ poll_freewait((table)) \ ) #else // } { #define compat_poll_initwait(wait, table) ( \ (wait) = (table), /* confuse compiler */ \ (wait) = (poll_table *) __get_free_page(GFP_KERNEL), \ (wait)->nr = 0, \ (wait)->entry = (struct poll_table_entry *)((wait) + 1), \ (wait)->next = NULL \ ) static inline void poll_freewait(poll_table *wait) { while (wait) { struct poll_table_entry * entry; poll_table *old; entry = wait->entry + wait->nr; while (wait->nr > 0) { wait->nr--; entry--; remove_wait_queue(entry->wait_address, &entry->wait); compat_fput(entry->filp); } old = wait; wait = wait->next; free_page((unsigned long) old); } } #define compat_poll_freewait(wait, table) ( \ poll_freewait((wait)) \ ) #endif // } /* * The wait_event_interruptible_timeout() interface is not * defined in pre-2.6 kernels. */ #ifndef wait_event_interruptible_timeout #define __wait_event_interruptible_timeout(wq, condition, ret) \ do { \ wait_queue_t __wait; \ init_waitqueue_entry(&__wait, current); \ \ add_wait_queue(&wq, &__wait); \ for (;;) { \ set_current_state(TASK_INTERRUPTIBLE); \ if (condition) \ break; \ if (!signal_pending(current)) { \ ret = schedule_timeout(ret); \ if (!ret) \ break; \ continue; \ } \ ret = -ERESTARTSYS; \ break; \ } \ set_current_state(TASK_RUNNING); \ remove_wait_queue(&wq, &__wait); \ } while (0) #define wait_event_interruptible_timeout(wq, condition, timeout) \ ({ \ long __ret = timeout; \ if (!(condition)) \ __wait_event_interruptible_timeout(wq, condition, __ret); \ __ret; \ }) #endif /* * The wait_event_timeout() interface is not * defined in pre-2.6 kernels. */ #ifndef wait_event_timeout #define __wait_event_timeout(wq, condition, ret) \ do { \ wait_queue_t __wait; \ init_waitqueue_entry(&__wait, current); \ \ add_wait_queue(&wq, &__wait); \ for (;;) { \ set_current_state(TASK_UNINTERRUPTIBLE); \ if (condition) \ break; \ ret = schedule_timeout(ret); \ if (!ret) \ break; \ } \ set_current_state(TASK_RUNNING); \ remove_wait_queue(&wq, &__wait); \ } while (0) #define wait_event_timeout(wq, condition, timeout) \ ({ \ long __ret = timeout; \ if (!(condition)) \ __wait_event_timeout(wq, condition, __ret); \ __ret; \ }) #endif /* * DEFINE_WAIT() and friends were added in 2.5.39 and backported to 2.4.28. * * Unfortunately it is not true. While some distros may have done it the * change has never made it into vanilla 2.4 kernel. Instead of testing * particular kernel versions let's just test for presence of DEFINE_WAIT * when figuring out whether we need to provide replacement implementation * or simply alias existing one. */ #ifndef DEFINE_WAIT # define COMPAT_DEFINE_WAIT(_wait) \ DECLARE_WAITQUEUE(_wait, current) # define compat_init_prepare_to_wait(_sleep, _wait, _state) \ do { \ __set_current_state(_state); \ add_wait_queue(_sleep, _wait); \ } while (0) # define compat_cont_prepare_to_wait(_sleep, _wait, _state) \ set_current_state(_state) # define compat_finish_wait(_sleep, _wait, _state) \ do { \ __set_current_state(_state); \ remove_wait_queue(_sleep, _wait); \ } while (0) #else # define COMPAT_DEFINE_WAIT(_wait) \ DEFINE_WAIT(_wait) # define compat_init_prepare_to_wait(_sleep, _wait, _state) \ prepare_to_wait(_sleep, _wait, _state) # define compat_cont_prepare_to_wait(_sleep, _wait, _state) \ prepare_to_wait(_sleep, _wait, _state) # define compat_finish_wait(_sleep, _wait, _state) \ finish_wait(_sleep, _wait) #endif /* #ifndef DEFINE_WAIT */ #endif /* __COMPAT_WAIT_H__ */ vmhgfs-only/driver-config.h0000444000000000000000000000425012025726746014746 0ustar rootroot/********************************************************* * Copyright (C) 1998 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 * *********************************************************/ /* * Sets the proper defines from the Linux header files * * This file must be included before the inclusion of any kernel header file, * with the exception of linux/autoconf.h and linux/version.h --hpreg */ #ifndef __VMX_CONFIG_H__ #define __VMX_CONFIG_H__ #define INCLUDE_ALLOW_VMCORE #define INCLUDE_ALLOW_VMMON #define INCLUDE_ALLOW_MODULE #define INCLUDE_ALLOW_VMNIXMOD #include "includeCheck.h" #include #include "compat_version.h" /* * We rely on Kernel Module support. Check here. */ #ifndef CONFIG_MODULES # error "No Module support in this kernel. Please configure with CONFIG_MODULES" #endif /* * 2.2 kernels still use __SMP__ (derived from CONFIG_SMP * in the main Makefile), so we do it here. */ #ifdef CONFIG_SMP # define __SMP__ 1 #endif #if defined(CONFIG_MODVERSIONS) && defined(KERNEL_2_1) # if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,60) /* * MODVERSIONS might be already defined when using kernel's Makefiles. */ # ifndef MODVERSIONS # define MODVERSIONS # endif # include # endif #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) /* * Force the uintptr_t definition to come from linux/types.h instead of vm_basic_types.h. */ # include # define _STDINT_H 1 #endif #ifndef __KERNEL__ # define __KERNEL__ #endif #endif vmhgfs-only/Makefile.normal0000444000000000000000000000654512025726746014777 0ustar rootroot#!/usr/bin/make -f ########################################################## # Copyright (C) 1998 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 # ########################################################## #### #### VMware vmhgfs Makefile to be distributed externally #### vm_check_build = $(shell if $(CC) $(CC_OPTS) $(INCLUDE) -Werror -S -o /dev/null -xc $(1) \ > /dev/null 2>&1; then echo "$(2)"; else echo "$(3)"; fi) DRIVERNAME = $(DRIVER)-$(VM_UNAME) ifneq (,$(filter x86_64%, $(shell $(CC) -dumpmachine))) MACHINE := x86_64 else MACHINE := x386 endif #### #### You must compile with at least -O level of optimization #### or the module won't load. #### If desparate, I think that bringing in might #### suffice. #### CC_WARNINGS := -Wall -Wstrict-prototypes # Don't use -pipe or egcs-2.91.66 (shipped with RedHat) will die CC_KFLAGS := -D__KERNEL__ -fno-strength-reduce -fno-omit-frame-pointer \ -fno-common -DKBUILD_MODNAME=$(DRIVER) CC_KFLAGS += $(call vm_check_gcc,-falign-loops=2 -falign-jumps=2 -falign-functions=2, \ -malign-loops=2 -malign-jumps=2 -malign-functions=2) CC_KFLAGS += $(call vm_check_gcc,-fno-strict-aliasing,) ifeq ($(MACHINE),x86_64) CC_KFLAGS += -mno-red-zone -mcmodel=kernel else # Gcc 3.0 deprecates -m486 --hpreg CC_KFLAGS += -DCPU=586 $(call check_gcc,-march=i586,-m486) endif CC_OPTS := -O2 -DMODULE $(GLOBAL_DEFS) $(CC_KFLAGS) $(CC_WARNINGS) INCLUDE := -I. -I$(HEADER_DIR) INCLUDE += $(shell $(CC) $(INCLUDE) -E $(SRCROOT)/autoconf/geninclude.c \ | sed -n -e 's!^APATH!-I$(HEADER_DIR)/asm!p') CC_OPTS += $(call vm_check_build, $(SRCROOT)/autoconf/setnice.c, -DVMW_HAVE_SET_USER_NICE, ) CC_OPTS += $(call vm_check_build, $(SRCROOT)/autoconf/epoll.c, -DVMW_HAVE_EPOLL, ) CC_OPTS += $(call vm_check_build, $(SRCROOT)/autoconf/maxbytes.c, -DVMW_SB_HAS_MAXBYTES, ) CC_OPTS += -DVMW_KMEMCR_HAS_DTOR # Core driver components OBJS = bdhandler.o OBJS += dentry.o OBJS += dir.o OBJS += file.o OBJS += filesystem.o OBJS += fsutil.o OBJS += inode.o OBJS += link.o OBJS += module.o OBJS += page.o OBJS += request.o OBJS += stubs.o OBJS += super.o # Additional components OBJS += cpName.o OBJS += cpNameLinux.o OBJS += cpNameLite.o OBJS += hgfsUtil.o OBJS += staticEscape.o OBJS += backdoor.o OBJS += hgfsBd.o OBJS += kernelStubsLinux.o OBJS += message.o OBJS += messageBackdoor.o OBJS += rpcout.o ifeq ($(MACHINE),x86_64) OBJS += backdoorGcc64.o else OBJS += backdoorGcc32.o endif CFLAGS := $(CC_OPTS) $(INCLUDE) LIBS := default: all all: ../$(DRIVER).o $(DRIVERNAME): $(OBJS) $(LD) -r -o $@ $^ $(DRIVER) $(DRIVER).o ../$(DRIVER).o: $(DRIVERNAME) cp -f $< $@ auto-build: ../$(DRIVER).o clean: rm -f $(DRIVERNAME) ../$(DRIVERNAME) $(DRIVER).o ../$(DRIVER).o $(DRIVER) $(OBJS) .SILENT: vmhgfs-only/Makefile.kernel0000444000000000000000000000457112025726746014764 0ustar rootroot#!/usr/bin/make -f ########################################################## # Copyright (C) 1998 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 # ########################################################## #### #### VMware vmhgfs Makefile to be distributed externally #### INCLUDE := -I. EXTRA_CFLAGS := $(CC_OPTS) $(INCLUDE) EXTRA_CFLAGS += $(call vm_check_build, $(SRCROOT)/autoconf/setnice.c, -DVMW_HAVE_SET_USER_NICE, ) EXTRA_CFLAGS += $(call vm_check_build, $(SRCROOT)/autoconf/epoll.c, -DVMW_HAVE_EPOLL, ) EXTRA_CFLAGS += $(call vm_check_build, $(SRCROOT)/autoconf/maxbytes.c, -DVMW_SB_HAS_MAXBYTES, ) EXTRA_CFLAGS += $(call vm_check_build, $(SRCROOT)/autoconf/cachecreate.c, -DVMW_KMEMCR_HAS_DTOR, ) EXTRA_CFLAGS += $(call vm_check_build, $(SRCROOT)/autoconf/cachector.c, -DVMW_KMEMCR_CTOR_HAS_3_ARGS, ) EXTRA_CFLAGS += $(call vm_check_build, $(SRCROOT)/autoconf/cachector1.c, -DVMW_KMEMCR_CTOR_HAS_2_ARGS, ) # Note: These tests are inverted EXTRA_CFLAGS += $(call vm_check_build, $(SRCROOT)/autoconf/getsb1.c,, -DVMW_GETSB_2618) EXTRA_CFLAGS += $(call vm_check_build, $(SRCROOT)/autoconf/statfs1.c,, -DVMW_STATFS_2618) EXTRA_CFLAGS += $(call vm_check_build, $(SRCROOT)/autoconf/inode1.c,, -DVMW_INODE_2618) obj-m += $(DRIVER).o $(DRIVER)-y := $(subst $(SRCROOT)/, , $(patsubst %.c, %.o, $(wildcard $(SRCROOT)/*.c))) # # On a 32-bit machine, strip out 64-bit backdoor code, and vice versa. # ifeq ($(CONFIG_X86_64),y) $(DRIVER)-y := $(filter-out backdoorGcc32.o, $($(DRIVER)-y)) else $(DRIVER)-y := $(filter-out backdoorGcc64.o, $($(DRIVER)-y)) endif clean: rm -rf $(wildcard $(DRIVER).mod.c $(DRIVER).ko .tmp_versions \ Module.symvers Modules.symvers Module.markers modules.order \ $(foreach dir,./,$(addprefix $(dir),.*.cmd .*.o.flags *.o))) vmhgfs-only/Makefile0000444000000000000000000000726412025726746013507 0ustar rootroot#!/usr/bin/make -f ########################################################## # Copyright (C) 1998 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 # ########################################################## #### #### VMware kernel module Makefile to be distributed externally #### #### #### SRCROOT _must_ be a relative path. #### SRCROOT = . VM_UNAME = $(shell uname -r) # Header directory for the running kernel HEADER_DIR = /lib/modules/$(VM_UNAME)/build/include BUILD_DIR = $(HEADER_DIR)/.. DRIVER := vmhgfs PRODUCT := @PRODUCT@ # Grep program GREP = /bin/grep vm_check_gcc = $(shell if $(CC) $(1) -S -o /dev/null -xc /dev/null \ > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi) vm_check_file = $(shell if test -f $(1); then echo "yes"; else echo "no"; fi) ifndef VM_KBUILD VM_KBUILD := no ifeq ($(call vm_check_file,$(BUILD_DIR)/Makefile), yes) ifneq ($(call vm_check_file,$(BUILD_DIR)/Rules.make), yes) VM_KBUILD := 26 endif endif export VM_KBUILD endif ifndef VM_KBUILD_SHOWN ifeq ($(VM_KBUILD), no) VM_DUMMY := $(shell echo >&2 "Using standalone build system.") else ifeq ($(VM_KBUILD), 24) VM_DUMMY := $(shell echo >&2 "Using 2.4.x kernel build system.") else VM_DUMMY := $(shell echo >&2 "Using 2.6.x kernel build system.") endif endif VM_KBUILD_SHOWN := yes export VM_KBUILD_SHOWN endif ifneq ($(VM_KBUILD), no) VMCCVER := $(shell $(CC) -dumpversion) # If there is no version defined, we are in toplevel pass, not yet in kernel makefiles... ifeq ($(VERSION),) ifeq ($(VM_KBUILD), 24) DRIVER_KO := $(DRIVER).o else DRIVER_KO := $(DRIVER).ko endif .PHONY: $(DRIVER_KO) auto-build: $(DRIVER_KO) cp -f $< $(SRCROOT)/../$(DRIVER).o # $(DRIVER_KO) is a phony target, so compare file times explicitly $(DRIVER): $(DRIVER_KO) if [ $< -nt $@ ] || [ ! -e $@ ] ; then cp -f $< $@; fi # Pass gcc version down the chain, so we can detect if kernel attempts to use unapproved compiler VM_CCVER := $(VMCCVER) export VM_CCVER VM_CC := $(CC) export VM_CC MAKEOVERRIDES := $(filter-out CC=%,$(MAKEOVERRIDES)) # # Define a setup target that gets built before the actual driver. # This target may not be used at all, but if it is then it will be defined # in Makefile.kernel # prebuild:: ; postbuild:: ; $(DRIVER_KO): prebuild make -C $(BUILD_DIR) SUBDIRS=$$PWD SRCROOT=$$PWD/$(SRCROOT) modules make -C $$PWD SRCROOT=$$PWD/$(SRCROOT) postbuild endif vm_check_build = $(shell if $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) \ $(CPPFLAGS) $(CFLAGS) $(CFLAGS_KERNEL) $(LINUXINCLUDE) \ $(EXTRA_CFLAGS) -Iinclude2/asm/mach-default \ -DKBUILD_BASENAME=\"$(DRIVER)\" \ -Werror -S -o /dev/null -xc $(1) \ > /dev/null 2>&1; then echo "$(2)"; else echo "$(3)"; fi) CC_WARNINGS := -Wall -Wstrict-prototypes CC_OPTS := $(GLOBAL_DEFS) $(CC_WARNINGS) -DVMW_USING_KBUILD ifdef VMX86_DEVEL CC_OPTS += -DVMX86_DEVEL endif ifdef VMX86_DEBUG CC_OPTS += -DVMX86_DEBUG endif include $(SRCROOT)/Makefile.kernel ifdef TOPDIR ifeq ($(VM_KBUILD), 24) O_TARGET := $(DRIVER).o obj-y := $($(DRIVER)-y) include $(TOPDIR)/Rules.make endif endif else include $(SRCROOT)/Makefile.normal endif #.SILENT: vmhgfs-only/COPYING0000444000000000000000000004310312025726745013071 0ustar rootroot GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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; either version 2 of the License, or (at your option) any 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License.